#树上差分 or 01-Trie#洛谷 6623 [省选联考 2020 A 卷] 树

题目


分析(01trie)

考虑用trie做需要满足什么操作:加入某个数、01-Trie的合并、全局加一。

主要是全局加一比较难做,考虑改变的地方就是 \(X*2^T+2^T-1\)

把01-Trie倒着建,那么全局加一只需要交换左右儿子并往原来的右儿子更新就可以了

再考虑这样建如何维护信息,那么就是 \(w[trie[x][0]]*2\) \(xor\) \(w[trie[x][1]]*2\) xor \(cnt[trie[x][1]]\)

也许这个能更好启发树上差分的做法吧


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=530011,M=N*21; long long Ans;
struct node{int y,next;}e[N]; bool _w[M];
int ans[N],a[N],as[N],n,cnt,trie[M][2],rt[N],w[M];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void pup(int rt){
	_w[rt]=_w[trie[rt][0]]^_w[trie[rt][1]];
	w[rt]=(w[trie[rt][0]]^w[trie[rt][1]])<<1|_w[trie[rt][1]];
}
void update(int &rt,int x,int z){
	if (!rt) rt=++cnt;
	if (z>20){
	    _w[rt]^=1;
	    return;
	}
	update(trie[rt][(x>>z)&1],x,z+1);
	pup(rt);
}
void one(int rt){
	swap(trie[rt][0],trie[rt][1]);
	if (trie[rt][0]) one(trie[rt][0]);
	pup(rt);
}
int Merge(int fi,int se){
	if (!fi||!se) return fi|se;
	w[fi]^=w[se],_w[fi]^=_w[se];
	trie[fi][0]=Merge(trie[fi][0],trie[se][0]);
	trie[fi][1]=Merge(trie[fi][1],trie[se][1]);
	return fi;
}
void dfs(int x){
	for (int i=as[x];i;i=e[i].next)
	    dfs(e[i].y),rt[x]=Merge(rt[x],rt[e[i].y]);
	one(rt[x]),update(rt[x],a[x],0);
	ans[x]=w[rt[x]];
}
int main(){
	n=iut();
	for (int i=1;i<=n;++i) a[i]=iut();
	for (int i=2;i<=n;++i){
		int x=iut();
		e[i]=(node){i,as[x]},as[x]=i;
	}
	dfs(1);
	for (int i=1;i<=n;++i) Ans+=ans[i];
	return !printf("%lld",Ans);
}

分析(树上差分)

改变的地方就是 \(X*2^T+2^T-1\),那么可以拆位考虑,

将子树的答案合并上来时,只有这些位置会改变。

如果记 \(a'_x=a_x+dep_x\),那么对于每个二进制位 子树中 \(dep_x\) 这个位置实则是要改变的,

因为插入 \(a_y+dep_x+1\) 后如果该值与 \(dep_x\) 在某个二进制位下与 \(2^i-1\) 同余那么它需要改变

树上差分就是用总的改变位置异或非子树改变位置即可


代码

#include <cstdio>
#include <cctype>
using namespace std;
const int N=530011; struct node{int y,next;}e[N];
int ans[N],a[N],as[N],w[N<<1][21],n,two[21]; long long Ans;
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void dfs(int x,int d){
	ans[x]=a[x];
	for (int i=0;i<21;++i) w[(d+a[x])&(two[i]-1)][i]^=two[i];
	for (int i=0;i<21;++i) ans[x]^=w[d&(two[i]-1)][i];
	for (int i=as[x];i;i=e[i].next)
	    dfs(e[i].y,d+1),ans[x]^=ans[e[i].y];
	for (int i=0;i<21;++i) ans[x]^=w[d&(two[i]-1)][i];
}
int main(){
	n=iut(),two[0]=1;
	for (int i=1;i<21;++i) two[i]=two[i-1]<<1;
	for (int i=1;i<=n;++i) a[i]=iut();
	for (int i=2;i<=n;++i){
		int x=iut();
		e[i]=(node){i,as[x]},as[x]=i;
	}
	dfs(1,0);
	for (int i=1;i<=n;++i) Ans+=ans[i];
	return !printf("%lld",Ans);
}
posted @ 2021-11-12 16:48  lemondinosaur  阅读(68)  评论(0)    收藏  举报