树状数组
树状数组
在去年就会写会用了,记得去省选的时候66.7%的分数是靠树状数组得的。
用法1:
最开始用它是用它求逆序对,当前已经读了i个元素,比a[i]小的有query(i)个,那么比a[i]大而且比a[i]先读入的有i-query(a[i])个;
#include<bits/stdc++.h> using namespace std; long long n,num,ans; long long t[500100]; long long a[500100],b[500100]; void in(long long &x) { char c=getchar();x=0; while(c<'0'||c>'9')c=getchar(); while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar(); } void modify(long long k) { for(;k<=n;k+=(-k)&k) t[k]+=1; } long long getnum(long long k) { long long cnt=0; for(;k>0;k-=k&(-k)) cnt+=t[k]; return cnt; } int main() { in(n); for(long long i=1;i<=n;i++) { in(a[i]); b[i]=a[i]; } sort(b+1,b+n+1); unique(b+1,b+n+1); for(long long i=1;i<=n;i++) a[i]=lower_bound(b+1,b+n+1,a[i])-b; for(long long i=1;i<=n;i++) { modify(a[i]); ans+=i-getnum(a[i]); } cout<<ans; return 0; }
用法2:
单点修改,区间求和
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cmath> #include<ctime> #include<cstring> #define inf 2147483647 #define For(i,a,b) for(register long long i=a;i<=b;i++) #define p(a) putchar(a) #define g() getchar() //by war //2017.10.13 using namespace std; long long t[1000000]; long long a[1000000]; long long n,m; long long x,y; void in(long long &x) { long long y=1; char c=g();x=0; while(c<'0'||c>'9') { if(c=='-') y=-1; c=g(); } while(c<='9'&&c>='0')x=x*10+c-'0',c=g(); x*=y; } void o(long long x) { if(x<0) { p('-'); x=-x; } if(x>9)o(x/10); p(x%10+'0'); } void modify(long long k,long long change) { for(;k<=n;k+=k&(-k)) t[k]+=change; } long long gn(long long k) { long long cnt=0; for(;k>0;k-=(-k)&k) cnt+=t[k]; return cnt; } int main() { in(n),in(m); For(i,1,n) { in(a[i]); modify(i,a[i]); } For(i,1,m) { in(x); if(x==1) { in(x),in(y); modify(x,y); } else { in(x),in(y); o(gn(y)-gn(x-1)),p('\n'); } } return 0; }
用法3:区间修改,单点查询
这个用到了差分的思想。
有点像数列的裂项相消,
a[n]==(a[n]-a[n-1])+(a[n-1]-a[n-2])+...+(a[0]-0);
比如n==5
原数列: 1 2 3 4 5
差分数列:1 1 1 1 1
l==2,r==4,这一段区间加上2
则序列变成了
1 4 5 6 5
1 3 1 1 -1
显然,实际上就是a[l]+=k,a[r+1]-=k;
单点查询就是求前缀和。
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cmath> #include<ctime> #include<cstring> #define inf 2147483647 #define For(i,a,b) for(register int i=a;i<=b;i++) #define p(a) putchar(a) #define g() getchar() using namespace std; int n,m,x,y,l,r,pre; int t[500010]; inline void in(int &x) { int y=1; char c=g();x=0; while(c<'0'||c>'9') { if(c=='-') y=-1; c=g(); } while(c<='9'&&c>='0')x=x*10+c-'0',c=g(); x*=y; } inline void o(int x) { if(x<0) { p('-'); x=-x; } if(x>9)o(x/10); p(x%10+'0'); } inline void modify(int k,int change) { for(;k<=n;k+=(-k)&k) t[k]+=change; } inline int getnum(int k) { int ans=0; for(;k>0;k-=(-k)&k) ans+=t[k]; return ans; } int main() { in(n),in(m); For(i,1,n) { in(x); modify(i,x-pre); pre=x; } For(i,1,m) { in(x); if(x==1) { in(l),in(r),in(x); modify(l,x); modify(r+1,-x); } else { in(x); o(getnum(x)),p('\n'); } } return 0; }
树状数组最后一波操作:
区间修改和区间求和
这时候要维护两个数组
t[]表示原来的差分数组
前缀和:
sum(1,n)
=a[1]+a[2]+a[3]...+a[n]
=t[1]+(t[1]+t[2])+...+
a[1]+a[2]+..+a[n]
=t[1]+t[1]+t[2]+..+(t[1]+..+t[n])
=n*(t[1]+..+t[n])-(0*t[1]+1*t[2]+..+(n-1)*t[n])
我令t1[i]=(i-1)*t[i];
1 2 3 4 5
1 1 1 1 1
0 1 2 3 4
l=2,r=4,区间加2
1 4 5 6 5
1 3 1 1 -1
0 3 2 3 -4
+1*2 -4*2
5 2
1 2 3 4 5
1 2 4 2
2 1 5
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cmath> #include<ctime> #include<cstring> #define inf 2147483647 #define For(i,a,b) for(register long long i=a;i<=b;i++) #define p(a) putchar(a) #define g() getchar() //by war //2017.10.14 //Ê÷×´Êý×éʵÏÖÇø¼äÐ޸ĺÍÇø¼äÇóºÍ²Ù×÷ //1 l r k //2 l r using namespace std; long long n,m,l,r,pre,t[100010],t1[100010],k,f,x; void in(long long &x) { long long y=1; char c=g();x=0; while(c<'0'||c>'9') { if(c=='-') y=-1; c=g(); } while(c<='9'&&c>='0')x=x*10+c-'0',c=g(); x*=y; } void o(long long x) { if(x<0) { p('-'); x=-x; } if(x>9)o(x/10); p(x%10+'0'); } void add(long long *t,long long k,long long c) { for(;k<=n;k+=(-k)&k) t[k]+=c; } long long get(long long *t,long long k) { long long ans=0; for(;k>0;k-=(-k)&k) ans+=t[k]; return ans; } long long sum(long long n) { return n*get(t,n)-get(t1,n); } int main() { in(n),in(m); For(i,1,n) { in(x); add(t,i,x-pre); add(t1,i,(i-1)*(x-pre)); pre=x; } For(i,1,m) { in(f); if(f==1) { in(l),in(r),in(k); add(t,l,k); add(t,r+1,-k); add(t1,l,(l-1)*k); add(t1,r+1,r*(-k)); } else { in(l),in(r); o(sum(r)-sum(l-1)),p('\n'); } } return 0; }