题解:P11217 【MX-S4-T1】「yyOI R2」youyou 的垃圾桶
题目链接
https://www.luogu.com.cn/problem/P11217
分析
先不考虑维护垃圾桶的攻击力,假设我们已经知道了所有垃圾桶的攻击力。
翻倍 操作可以用左移(<<
)实现。
首先先计算出所有垃圾桶的伤害值,然后看看能抗几个整轮。
然后考虑不能抗的情况。由于所有垃圾桶的攻击力都为正数,所以可以二分最后一个攻击力前缀和 \(\le\) 剩余生命值的位置。
可以发现我们需要能够快速实现区间修改和查询区间和的数据结构。线段树和树状数组都满足要求,但线段树常数太大,会 T,所以这里使用树状数组。
Code
#include<bits/stdc++.h>
#define i64 long long
using namespace std;
const int N=2e5+5;
int n,q,a[N];
i64 w,tr1[N],tr2[N];
//快读
char ch;
void read(int &x){
x=0;ch=getchar();
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57){
x=(x<<3)+(x<<1)+ch-48;
ch=getchar();
}
return;
}
//树状数组
int lowbit(int x){return x&(-x);}
i64 query(int x){
i64 res=0;
for(int i=x;i;i-=lowbit(i))
res+=(x+1)*tr1[i]-tr2[i];
return res;
}
void upd(int x,i64 v){
for(int i=x;i<=n;i+=lowbit(i))
tr1[i]+=v,tr2[i]+=x*v;
return;
}
//区间查询
i64 query_ran(int l,int r){return query(r)-query(l-1);}
//区间修改
void upd_ran(int l,int r,i64 v){upd(l,v),upd(r+1,-v);}
//二分
int bin_sch(i64 x,i64 k){
int l=0,r=n,mid,ans=0;
while(l<=r){
mid=l+r>>1;
if((x>>k)>query_ran(1,mid))l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
int main(){
// freopen("wxyt.in","r",stdin);
// freopen("wxyt.out","w",stdout);
cin>>n>>q>>w;
for(int i=1;i<=n;++i){
read(a[i]);
upd_ran(i,i,a[i]);
}
int l,r,d;
i64 k,num,tmp,ans;
while(q--){
read(l),read(r),read(d);
upd_ran(l,r,d);
k=0,ans=0,num=w,tmp=query_ran(1,n);
while(true){
//整轮
if((num>>k)>tmp){ans+=n,num-=(tmp<<k),++k;}
//不是整轮,二分找到最后一个能扛住的位置
else{
tmp=bin_sch(num,k);
if(tmp)ans+=tmp;
break;
}
}
printf("%lld\n",ans);
}
return 0;
}