树状数组

ooliver OI

树状数组

P3605 [USACO17JAN] Promotion Counting P

很模板的一道树上树状数组, 计算遍历子树后的变化就可以了, 代码:

#include
using namespace std;

#define lowbit(x) x&-x
const int N=2e5+5;
int n,m;
vector g[N];
int a[N],tr[N],cnt[N],p[N];

void change(int x,int k){
	while(x>n;
	for(int i=1;i>p[i];
		a[i]=p[i];
	}
	sort(p+1,p+1+n);
	for(int i=1;i>u; 
		g[u].push_back(i);
	} 
	dfs1(1);
	for(int i=1;i
`#include
using namespace std;

#define lowbit(x) x&-x
const int N=2e5+5;
int n,m;
vectorint> g[N];
int a[N],tr[N],cnt[N],p[N];

void change(int x,int k){
	while(x
		tr[x]+=k;
		x+=lowbit(x);
	}
}

int query(int x){
	int t=0;
	while(x){
		t+=tr[x];
		x-=lowbit(x);
	}
	return t;
}

void dfs1(int x){
	cnt[x]-=(query(n)-query(a[x]));
	for(int i=0;ig[x].size();i++) dfs1(g[x][i]);
	cnt[x]+=(query(n)-query(a[x]));
	change(a[x],1);
}

signed main(){
	cin>>n;
	for(int i=1;i
		cin>>p[i];
		a[i]=p[i];
	}
	sort(p+1,p+1+n);
	for(int i=1;ia[i]=lower_bound(p+1,p+1+n,a[i])-p;
	for(int i=2;i
		int u;
		cin>>u; 
		g[u].push_back(i);
	} 
	dfs1(1);
	for(int i=1;icnt[i]
    return 0;
}`

P1972 [SDOI2009] HH 的项链

考虑离线处理,对每次查询按右端点大小排序。 然后从左到右枚举每个贝壳, 如果之前出现了相同的贝壳种类, 由于右端点排序的性质,当前贝壳一定会查询到, 所以给当前贝壳加一,给之前的贝壳减一,来消除区间求和找到它的影响即可。 代码:

#include
using namespace std;

#define lowbit(x) x&-x
const int N=1e6+5;
int n,m;
int tr[N],a[N],last[N],ans[N];
struct ask{
	int l,r,id;
}q[N];

void change(int x,int k){
	while(x>n;
	for(int i=1;i>a[i];
	cin>>m;
	for(int i=1;i>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int l=0;
	for(int i=1;i
`#include
using namespace std;

#define lowbit(x) x&-x
const int N=1e6+5;
int n,m;
int tr[N],a[N],last[N],ans[N];
struct ask{
	int l,r,id;
}q[N];

void change(int x,int k){
	while(x
		tr[x]+=k;
		x+=lowbit(x);
	}
}

int query(int x){
	int t=0;
	while(x){
		t+=tr[x];
		x-=lowbit(x);
	}
	return t;
}

bool cmp(ask x,ask y){return x.ry.r;}

signed main(){
	cin>>n;
	for(int i=1;i>a[i];
	cin>>m;
	for(int i=1;i>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int l=0;
	for(int i=1;i
		while(lq[i].r){
			l++;
			if(last[a[l]]) change(last[a[l]],-1);
			last[a[l]]=l;
			change(l,1);
		}
		ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
	}
	for(int i=1;ians[i]
    return 0;
}`

P4113 [HEOI2012] 采花

思路和上一题很相似,只不过题目改成了区间内同颜色数量大于 2 的颜色个数。 一样的离线+排序,发现从左往右找找到第二个同颜色的点才会对答案有贡献, 所以对于当前点,上一个同颜色的点 +1,上上一个同颜色的点 -1 消除影响即可。 代码:

#include
using namespace std;

#define lowbit(x) x&-x
const int N=2e6+5;
int n,m,c;
int tr[N],a[N],last[N],last2[N],ans[N];
struct ask{
	int l,r,id;
}q[N];

void change(int x,int k){
	while(x>n>>c>>m;
	for(int i=1;i>a[i];
	for(int i=1;i>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int l=0;
	for(int i=1;i
`#include
using namespace std;

#define lowbit(x) x&-x
const int N=2e6+5;
int n,m,c;
int tr[N],a[N],last[N],last2[N],ans[N];
struct ask{
	int l,r,id;
}q[N];

void change(int x,int k){
	while(x
		tr[x]+=k;
		x+=lowbit(x);
	}
}

int query(int x){
	int t=0;
	while(x){
		t+=tr[x];
		x-=lowbit(x);
	}
	return t;
}

bool cmp(ask x,ask y){return x.ry.r;}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0); 
	cin>>n>>c>>m;
	for(int i=1;i>a[i];
	for(int i=1;i>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int l=0;
	for(int i=1;i
		while(lq[i].r){
			l++;
			if(!last[a[l]]){
				last[a[l]]=l;
				continue;
			}
			change(last[a[l]],1);
			if(last2[a[l]]) change(last2[a[l]],-1);
			last2[a[l]]=last[a[l]],last[a[l]]=l; 
		}
		ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
	}
	for(int i=1;ians[i]"\n";
    return 0;
}`

P4054 [JSOI2009] 计数问题

二维树状数组模板题,建 c 个树状数组即可,代码:

#include
using namespace std;

#define lowbit(x) x&-x
const int N=105;
int n,m,q;
int tr[N][N*3][N*3],a[N*3][N*3];

void change(int c,int x,int y,int k){
	for(int i=x;i=1;i-=lowbit(i)) for(int j=y;j>=1;j-=lowbit(j)) t+=tr[c][i][j];
	return t;
}

signed main(){
	cin>>n>>m;
	for(int i=1;i>a[i][j];
		change(a[i][j],i,j,1);
	}
	cin>>q;
	while(q--){
		int op;
		cin>>op;
		if(op==1){
			int x,y,c;
			cin>>x>>y>>c;
			change(a[x][y],x,y,-1);
			change(a[x][y]=c,x,y,1);
		}
		else{
			int x1,x2,y1,y2,c;
			cin>>x1>>x2>>y1>>y2>>c;
			cout
`#include
using namespace std;

#define lowbit(x) x&-x
const int N=105;
int n,m,q;
int tr[N][N*3][N*3],a[N*3][N*3];

void change(int c,int x,int y,int k){
	for(int i=x;ilowbit(i)) for(int j=y;jlowbit(j)) tr[c][i][j]+=k;
}

int query(int c,int x,int y){
	int t=0;
	for(int i=x;i>=1;i-=lowbit(i)) for(int j=y;j>=1;j-=lowbit(j)) t+=tr[c][i][j];
	return t;
}

signed main(){
	cin>>n>>m;
	for(int i=1;ifor(int j=1;j
		cin>>a[i][j];
		change(a[i][j],i,j,1);
	}
	cin>>q;
	while(q--){
		int op;
		cin>>op;
		if(op==1){
			int x,y,c;
			cin>>x>>y>>c;
			change(a[x][y],x,y,-1);
			change(a[x][y]=c,x,y,1);
		}
		else{
			int x1,x2,y1,y2,c;
			cin>>x1>>x2>>y1>>y2>>c;
			coutquery(c,x2,y2)-query(c,x1-1,y2)-query(c,x2,y1-1)+query(c,x1-1,y1-1)"\n";
		}
	}
    return 0;
}`

P4514 上帝造题的七分钟

这题需要直接对二维差分数组进行前缀和, 设 aa 为原数组,dd 为差分数组,sumsum 为前缀和数组,有:

a[x][y]=∑i=1x∑j=1yd[i][j]sum[x][y]=∑i=1x∑j=1ya[i][j]\begin{aligned} a[x][y]=\sum^{x}{i=1}\sum^{y}{j=1}d[i][j] \ sum[x][y]=\sum^{x}{i=1}\sum^{y}{j=1}a[i][j] \ \end{aligned}

sum[x][y]=∑i=1x∑j=1y∑k=1i∑l=1jd[k][l]=∑i=1x∑j=1y(x−i+1)(y−j+1)d[i][j]=∑i=1x∑j=1yi⋅j⋅d[i][j]−(y+1)∑i=1x∑j=1yi⋅d[i][j]−(x+1)∑i=1x∑j=1yj⋅d[i][j]+(x+1)(y+1)∑i=1x∑j=1yd[i][j] \begin{aligned} &sum[x][y] \ =&\sum^{x}{i=1}\sum^{y}{j=1}\sum^{i}{k=1}\sum^{j}{l=1}d[k][l] \ =&\sum^{x}{i=1}\sum^{y}{j=1}(x-i+1)(y-j+1)d[i][j] \ =&\sum^{x}{i=1}\sum^{y}{j=1}i\cdot j\cdot d[i][j] -(y+1)\sum^{x}{i=1}\sum^{y}{j=1}i\cdot d[i][j] -(x+1)\sum^{x}{i=1}\sum^{y}{j=1}j\cdot d[i][j] +(x+1)(y+1)\sum^{x}{i=1}\sum^{y}{j=1}d[i][j] \end{aligned}

然后维护四个二维树状数组即可。

#include
using namespace std;

#define lowbit(x) x&-x
const int N=2050;
int n,m;
char op;
int a,b,c,d,delta;
int tr1[N][N],tri[N][N],trj[N][N],trij[N][N];
void add(int x,int y,int k){
	for(int i=x;i0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) res+=(x+1)*(y+1)*tr1[i][j]-(x+1)*trj[i][j]-(y+1)*tri[i][j]+trij[i][j];
	return res;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); 
	while(cin>>op){
		if(op=='X') cin>>n>>m;
		else if(op=='L'){
			cin>>a>>b>>c>>d>>delta;
			add(a,b,delta),add(a,d+1,-delta),add(c+1,b,-delta),add(c+1,d+1,delta);
		}
		else if(op=='k'){
			cin>>a>>b>>c>>d;
			cout
`#include
using namespace std;

#define lowbit(x) x&-x
const int N=2050;
int n,m;
char op;
int a,b,c,d,delta;
int tr1[N][N],tri[N][N],trj[N][N],trij[N][N];
void add(int x,int y,int k){
	for(int i=x;ilowbit(i)) for(int j=y;jlowbit(j)) tr1[i][j]+=k,tri[i][j]+=k*x,trj[i][j]+=k*y,trij[i][j]+=k*x*y;
}
int check(int x,int y){
	int res=0;
	for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) res+=(x+1)*(y+1)*tr1[i][j]-(x+1)*trj[i][j]-(y+1)*tri[i][j]+trij[i][j];
	return res;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); 
	while(cin>>op){
		if(op=='X') cin>>n>>m;
		else if(op=='L'){
			cin>>a>>b>>c>>d>>delta;
			add(a,b,delta),add(a,d+1,-delta),add(c+1,b,-delta),add(c+1,d+1,delta);
		}
		else if(op=='k'){
			cin>>a>>b>>c>>d;
			coutcheck(c,d)-check(a-1,d)-check(c,b-1)+check(a-1,b-1)"\n";
		}
		else break;
	}
	return 0;
} `