題解:P11217 【MX-S4-T1】「yyOI R2」youyou 的垃圾桶

e-zhe發表於2024-12-02

連結

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

相關文章