GYM 102220 problem H Skyscraper 树状数组

将近一年前写的题目了 又翻出来看看

题目链接:H Skyscraper

思路:用线段树维护差分数组 

设b[i]=a[i]-a[i-1]  

如果 bi<=0 说明在完成i-1时可以顺便把i位置完成

如果 bi>0 说明完成i-1后至少还需要bi才能完成

那么我们维护两个树状数组 

第一个树状数组维护的是差分数组 bi  第二个树状数组维护的是ci(ci=bi && bi>0) 

修改操作就是常规的差分数组修改 

询问的话 就是 a_{l}+c_{l+1}+c_{l+2}.......+c_{r}

也就是 b_{1}+b_{2}+......+b_{l}+c_{l+1}+c_{l+2}+......c_{r}

#include
using namespace std;
const int N = 1e5+10;
typedef long long ll;
ll a[N],b[N];
ll p1[N],p2[N];
int n,m;
void add(int x,ll val,ll p[]){
	while(x<=n){
		p[x]+=val;
		x+=x&-x;
	}
}
ll query(int x,ll p[]){
	ll ret = 0;
	while(x){
		ret+=p[x];
		x-=x&-x;
	}
	return ret;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(p1,0,sizeof(p1));
		memset(p2,0,sizeof(p2));
		scanf("%d%d",&n,&m);
		for(int i = 1; i <= n; i++){
			scanf("%lld",&a[i]);
			b[i]=a[i]-a[i-1];
			add(i,b[i],p1);
			if(b[i]>0) add(i,b[i],p2);
		}
		for(int i = 1; i <= m; i++){
			int op,l,r,k;
			scanf("%d%d%d",&op,&l,&r);
			if(op==1){
				scanf("%d",&k);
				add(l,k,p1),add(r+1,-k,p1);
				if(b[l]>0) add(l,k,p2);
				else if(b[l]+k>0) add(l,b[l]+k,p2);
				if(b[r+1]>0){
					if(b[r+1]-k>0) add(r+1,-k,p2);
					else add(r+1,-b[r+1],p2);
				}
				b[l]+=k,b[r+1]-=k;
			}else{
				printf("%lld\n",query(l,p1)+query(r,p2)-query(l,p2));
			}
		}
	}
	return 0;
}

 

你可能感兴趣的:(树状数组,差分)