问:你这个乡村学校的小学生,为什么要学劼劼劼呢?
答:因为窝一下做了太多的树状数组,懒得一篇一篇写了。
问:既然线段树可以代替树状数组,那么为什么要树状数组呢?
答:树状数组好写啊Orz。
问:那我可以不看冗长的介绍过程吗?
答:如果机房的门要关了或者你急着要泡妹子,请跳至最后一目。
【简介】
首先普通的一维树状数组就不说了,如果你不知道,戳这。
那么窝萌阔以看到:普通的树状数组支持单点修改、片段和。
显然树状数组是解决逆序对问题的最好方法。
某大神表示阔以用树状数组来维护区间最值,不明觉厉~
inline void init() { CLR(arr,0); for(int i=1;i<=N;++i) for(int j=i;j<=N&&arr[j]<num[i];j+=lowbit(j)) arr[j]=num[i]; } inline int query(int L,int R) { int res=0; for(--L;L<R;){ if(R-lowbit(R)>=L){res=max(res,arr[R]);R-=lowbit(R);} else{res=max(res,num[R]);--R;} } return res; } inline void update(int x,int val) { int ori=num[x]; num[x]=val; if(val>=ori) for(int i=x;i<=N&&arr[i]<val;i+=lowbit(i)) arr[i]=val; else{ for(int i=x;i<=N&&arr[i]==ori;i+=lowbit(i)) { arr[i]=val; for(int j=lowbit(i)>>1;j;j>>=1) arr[i]=max(arr[i],arr[i-j]); } } }
那么树状数组可不可以支持区间修改呢?
让窝萌先来解决一个子问题:维护一个序列,支持区间修改、单点求值。
那么差分,令d[i]=a[i]-a[i-1],d[1]=a[1],那么显然[tex]a[i] = \sum\limits_{j = 1}^n {d[j]}[/tex]
修改区间[l,r],则可以通过前缀和得到:d[l]+=k,d[r+1]-=k。
那么要求区间和怎么办呢?
通过简单的推导可以得到:
[tex]\sum\limits_{i = 1}^x {a[i]} = \sum\limits_{i = 1}^x {d[i]*(x - i + 1) = (x + 1)*\sum\limits_{i = 1}^x {d[i]} - \sum\limits_{i = 1}^x {d[i]*i} }[/tex]
那么再开一个树状数组来维护d[i]*i就阔以辣!
接着看二维树状数组。
显然根据树状数组的定义再加一维就阔以了。
即令tree[i][j]为矩阵[i-lowbit(i)+1,j-lowbit(j)+1]~[i,j]的片段和。
对于二维树状数组的区间修改和求和,可以通过二维前缀和的性质推出。
这里的d[i]有另一个定义,表示i~n这一段的都加上d[i]。
那么扩展到二维,d[i][j]表示矩阵[i,j]~[n,m]的都加上d[i][j]。
修改矩阵[x1,y1]~[x2,y2],那么d[x1][y1]+=k,d[x1][y2+1]-=k,d[x2+1][y1]-=k,d[x2+1][y2+1]+=k。
求[1,1]~[x,y],就是p-q。
[tex]p = (x + 1)(y + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]} } + \sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]*i*j} }[/tex]
[tex]q = (x + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]*j + (y + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]} } } } *i[/tex]
[tex]
显然我讲的很不详细,所以戳这
(悄悄地告诉你其实窝前面也是抄的论文)
【例题】
BZOJ2789
首先看到一个贪心性质,即第一个串中第k次出现的某个字母对应的一定是第二个中第k次出现的。
那么求个逆序对就阔以了。
BZOJ2743
先求出每个元素下一个与它相同的是第几个。
显然如果[l,r]中有两个同样的元素,该元素的编号为i>=l,则next[i]<=r就阔以了。
那么对询问进行离线操作,其余的还是程序说得更清楚,所以开始看程序吧。
BZOJ1452
对于每种颜色开个二维树状数组就水过了。。。
BZOJ3132
论文题,看完了绝逼会做。
BZOJ3038
显然发现了一个神奇的性质:一个数只要开根了7次后就会变成1,然后就不变了。
那么就用并查集维护变成1的区间不用改了,然后用树状数组维护整个序列。
BZOJ1106
如果两个相同的数间有x个未消掉的数,那么就需要交换x次。
那么只要知道区间内只出现过一次的数的个数就阔以啦!
【Codes】
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M=1000010; int n,i; long long ans; char a[M],b[M]; int c[M],tree[M],now[30]; int q[27][M]; int query(int x){ int ans=0; for (int i=x;i>0;i-=i&-i)ans+=tree[i]; return ans; } void update(int x){ for (int i=x;i<=n;i+=i&-i)tree[i]++; } int main(){ scanf("%d",&n); scanf("%s",a+1); scanf("%s",b+1); for (i=1;i<=n;i++)q[a[i]-'A'][++q[a[i]-'A'][0]]=i; for (i=1;i<=n;i++)c[i]=q[b[i]-'A'][++now[b[i]-'A']]; for (i=n;i>=1;i--){ ans+=query(c[i]); update(c[i]); } printf("%lld\n",ans); }
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M=1000010; int n,m,p,i,l; int a[M],now[M],next[M],ans[M],tree[M]; struct shit{int l,r,id;}q[M]; int cmp(shit a,shit b){return a.l<b.l;} int query(int x){ int ans=0; for (int i=x;i>0;i-=i&-i)ans+=tree[i]; return ans; } void update(int x,int p){ for (int i=x;i<=n;i+=i&-i)tree[i]+=p; } int main(){ scanf("%d%d%d",&n,&p,&m); for (i=1;i<=n;i++)scanf("%d",&a[i]); for (i=n;i>0;i--)next[i]=now[a[i]],now[a[i]]=i; for (i=1;i<=p;i++)if (next[now[i]])update(next[now[i]],1); for (i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; sort(q+1,q+m+1,cmp); l=1; for (i=1;i<=m;i++){ while (l<q[i].l){ if (next[l])update(next[l],-1); if (next[next[l]])update(next[next[l]],1); l++; } ans[q[i].id]=query(q[i].r)-query(q[i].l-1); } for (i=1;i<=m;i++)printf("%d\n",ans[i]); }
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m,i,j,opt,q,x,y,ch,x1,x2,y1,y2; int a[310][310]; int tree[110][310][310]; void update(int opt,int x,int y,int p){ for (int i=x;i<=n;i+=i&-i) for (int j=y;j<=m;j+=j&-j) tree[opt][i][j]+=p; } int query(int opt,int x,int y){ int ans=0; for (int i=x;i;i-=i&-i) for (int j=y;j;j-=j&-j) ans+=tree[opt][i][j]; return ans; } int main(){ scanf("%d%d",&n,&m); for (i=1;i<=n;i++) for (j=1;j<=m;j++){ scanf("%d",&a[i][j]); update(a[i][j],i,j,1); } scanf("%d",&q); while (q--){ scanf("%d",&opt); if (opt==1){ scanf("%d%d%d",&x,&y,&ch); update(a[x][y],x,y,-1); a[x][y]=ch; update(a[x][y],x,y,1); }else{ scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&ch); printf("%d\n",query(ch,x2,y2)-query(ch,x1-1,y2)-query(ch,x2,y1-1)+query(ch,x1-1,y1-1)); } } }
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; char s[10]; int n,m,x1,x2,y1,y2,ch; int tree[2100][2100][4]; void update(int x,int y,int p){ for (int i=x;i<=n;i+=i&-i) for (int j=y;j<=m;j+=j&-j){ tree[i][j][0]+=p; tree[i][j][1]+=p*x; tree[i][j][2]+=p*y; tree[i][j][3]+=p*x*y; } } int query(int x,int y){ int ans=0; for (int i=x;i;i-=i&-i) for (int j=y;j;j-=j&-j) ans=ans+tree[i][j][0]*(x+1)*(y+1)-tree[i][j][1]*(y+1)-tree[i][j][2]*(x+1)+tree[i][j][3]; return ans; } int main(){ scanf("%s",s); scanf("%d%d",&n,&m); while (scanf("%s",s)!=EOF){ if (s[0]=='L'){ scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&ch); update(x1,y1,ch); update(x2+1,y1,-ch); update(x1,y2+1,-ch); update(x2+1,y2+1,ch); }else{ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); printf("%d\n",query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1)); } } }
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> typedef long long ll; using namespace std; int n,m,i,opt,x,y; int f[100010]; ll a[100010],tree[100010]; int search(int x){ if (!f[x])f[x]=x; if (f[x]!=x)f[x]=search(f[x]); return f[x]; } ll query(int x){ ll ans=0; for (int i=x;i;i-=i&-i)ans+=tree[i]; return ans; } void update(int x,ll y){ for (int i=x;i<=n;i+=i&-i)tree[i]+=y; } void solve(int x,int y){ for (int i=search(x);i<=y;i=search(i+1)){ ll tmp=(ll)sqrt(a[i]); update(i,tmp-a[i]); a[i]=tmp; if (a[i]<=1)f[i]=search(i+1); } } int main(){ scanf("%d",&n); for (i=1;i<=n;i++){ scanf("%lld",&a[i]); if (a[i]<=1)f[i]=i+1; update(i,a[i]); } scanf("%d",&m); for (i=1;i<=m;i++){ scanf("%d%d%d",&opt,&x,&y); if (x>y)swap(x,y); if (!opt)solve(x,y);else printf("%lld\n",query(y)-query(x-1)); } }
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,i,ans; int a[100010],tree[100010],pre[100010]; void update(int x,int y){ for (int i=x;i<=2*n;i+=i&-i)tree[i]+=y; } int query(int x){ int ans=0; for (int i=x;i;i-=i&-i)ans+=tree[i]; return ans; } int main(){ scanf("%d",&n); for (i=1;i<=2*n;i++)scanf("%d",&a[i]); for (i=1;i<=2*n;i++){ if (!pre[a[i]])pre[a[i]]=i,update(i,1); else{ ans+=query(i)-query(pre[a[i]]); update(pre[a[i]],-1); } } printf("%d\n",ans); }