gym102220H 差分+树状数组(区间修改和输出)
这题目很有意思,让我学会了树状数组的差分,更加深刻理解了树状数组
树状数组的差分写法
void add(int x,int k){ for (int i = x;i <= n;i += lowbit(i)) c[i] += k; } int sum(int x){ int ans = 0; for (int i = x;i > 0;i -= lowbit(i)) ans += c[i]; return ans; } int main(){ add(l,x); add(r+1,-x); int zhi=sum(l)//就是a[l]的数值,前缀和。 }
二维差分模板
ll c[4][N][N]; void update(int k,int x,int y,ll z){ k--; for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) c[k][i][j]+=z; } ll sum(int k,int x,int y){ ll ret=0; for(int i=x;i>=1;i-=lowbit(i)) for(int j=y;j>=1;j-=lowbit(j)) ret+=c[k][i][j]; return ret; } ll get(int x,int y){ return sum(0,x,y)*(ll)(x+1)*(y+1)-sum(1,x,y)*(ll)(y+1)-(ll)(x+1)*sum(2,x,y)+sum(3,x,y); } int main() { get(x2,y2)-get(x2,y1-1)-get(x1-1,y2)+get(x1-1,y1-1) ; update(1,x1,y1,w),update(1,x1,y2+1,-w); update(1,x2+1,y1,-w),update(1,x2+1,y2+1,w); update(2,x1,y1,w*x1),update(2,x2+1,y1,-w*(x2+1)); update(2,x1,y2+1,-w*x1),update(2,x2+1,y2+1,w*(x2+1)); update(3,x1,y1,w*y1),update(3,x2+1,y1,-w*y1); update(3,x1,y2+1,-w*(y2+1)),update(3,x2+1,y2+1,w*(y2+1)); update(4,x1,y1,w*x1*y1),update(4,x2+1,y1,-w*(x2+1)*y1); update(4,x1,y2+1,-w*x1*(y2+1)),update(4,x2+1,y2+1,w*(x2+1)*(y2+1)); }
题意:
很简单,输入n m
给n个a[i],代表每栋楼要造的高度
接下来m个操作
输入op
当op等于1的时候输入l,r,val
区间l,r之间增加val高度
当op等于2的时候输入l,r
求区间l,r,最小的工作时长(这个我语文不好,举例吧 1 3 2需要3个工作时长 1 3 2 5需要6个工作时长,1 3 2 5 -> 0 2 1 4 -> 0 1 0 3 -> 0 0 0 3 -> 0 0 0 2 -> 0 0 0 1 -> 0 0 0 0)
思路:
差分树状数组
a[i]原本的高度 b[i]=a[i]-a[i-1](前缀和) c[i]=b[i]>0?b[i]:0(后者比前者小,需要多出的工作时长)
所以答案就是 a[l]+(c[i]求和(l<i<r))
增加的val高度,a[l]用一个前缀和树状数组 add(val,l)add(-val,r+1)
维护c[i]的树状数组特殊判断(tql这里的思考判断,做题的时候就是这里没处理好,于是代码写不出来)
其实就是把每个建筑的递增高峰写出来,如果前者比后者大,后者那个位置就是0
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<stack> #include<algorithm> #include<stdio.h> #include<map> #include<set> #define ll long long #define lowbit(x) x&-x using namespace std; const int maxn=1e5+10; int n,m,a[maxn],b[maxn]; ll cnt[maxn],cn[maxn]; void add1(int zhi,int x){ for(int i=x;i<=n;i+=lowbit(i)){ cn[i]+=(ll)zhi; } } void add2(int zhi,int x){ for(int i=x;i<=n;i+=lowbit(i)){ cnt[i]+=(ll)zhi; } } ll geshu1(int x){ if(x==0){return 0;} ll sum=0; for(int i=x;i>0;i-=lowbit(i)){ sum+=cn[i]; } return sum; } ll geshu2(int x){ if(x==0){return 0;} ll sum=0; for(int i=x;i>0;i-=lowbit(i)){ sum+=cnt[i]; } return sum; } int main() { int t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=0;i<=n;i++){ cnt[i]=0,cn[i]=0; } for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[i]=a[i]-a[i-1];int tt=(b[i]>0?b[i]:0); add1(b[i],i); add2(tt,i); } for(int i=1;i<=m;i++){ int op; scanf("%d",&op); if(op==1){ int l,r,val; scanf("%d%d%d",&l,&r,&val); add1(val,l);add1(-val,r+1);//差分 if(b[l]<0){ int tt=-b[l]; if(tt<val){ add2(val-tt,l); } } else{ add2(val,l); } b[l]+=val; if(b[r+1]>=0){ int tt=min(b[r+1],val); add2(-tt,r+1); } b[r+1]-=val; } else{ int l,r; scanf("%d%d",&l,&r); printf("%lld\n",geshu2(r)-geshu2(l)+geshu1(l)); } } } return 0; }
看了扩展
区间修改,单点输出
单点修改,区间输出
到区间修改和输出
tql(看了Top_Spirit博客)
数组cnt[n] 用来维护a[i]
a[1]+a[2]+...+a[n]= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])
= n*c[1] + (n-1)*c[2] +... +c[n]
= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])
再维护一个数组cnt2[n],cnt2[i] = (i-1)*c[i]
a[1]+a[2]+...+a[n]=n*geshu1(cnt,n) - geshu2(cnt2,n)
add1( l , val); add1(r + 1, -val); add2( l , (l - 1) * val); add2 (r + 1, r* (-z));