这是一个句子测试

12
23
2015
0

论蒟蒻的花样作死之树状数组

问:你这个乡村学校的小学生,为什么要学劼劼劼呢?

答:因为窝一下做了太多的树状数组,懒得一篇一篇写了。

问:既然线段树可以代替树状数组,那么为什么要树状数组呢?

答:树状数组好写啊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);
}
Category: 论蒟蒻的花样作死系列 | Tags: | Read Count: 3500

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com