题解: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;
}
posted @ 2024-12-02 21:18  e_zhe  阅读(8)  评论(0编辑  收藏  举报