連結
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;
}