联合省选2020 树

给定一棵 \(n\) 个结点的有根树 \(T\),结点从 \(1\) 开始编号,根结点为 \(1\) 号结点,每个结点有一个正整数权值 \(v_i\)

\(x\) 号结点的子树内(包含 \(x\) 自身)的所有结点编号为 \(c_1, c_2, \dots, c_k\),定义 \(x\) 的价值为:

\[val(x) = (v_{c_1} + d(c_1, x)) \oplus (v_{c_2} + d(c_2, x)) \oplus \cdots \oplus (v_{c_k} + d(c_k, x)) \]

其中 \(d(x, y)\) 表示树上 \(x\) 号结点与 \(y\) 号结点间唯一简单路径所包含的边数,\(d(x, x) = 0\)\(\oplus\) 表示异或运算。

请你求出 \(\sum_{i=1}^n val(i)\) 的结果。

\(100\%\) 的数据:\(1\le n, v_i \le 525010, 1\le p_i\le n\)

Trie

https://blog.csdn.net/qq_39854734/article/details/106946271
https://blog.csdn.net/qq_33229466/article/details/106927385

考虑用 01-Trie 来维护所有\(v(c_j)+d(c_j,x)\),并启发式合并。

01-Trie 需要支持:插入一个数;求全局异或和;全局加一。

前两个操作容易维护(全局异或和需要每个节点记录其子树里所有数在一些位的异或和);

为了支持第三个操作,我们把数按从低位到高位放进 01-Trie,每次交换左右儿子,然后递归处理左(交换前是右)儿子,然后更新自己这个节点维护的异或和。

时间复杂度\(O(n\log n)\)

CO int N=53e4;
int a[N],sum[N][21];
vector<int> to[N];

int root[N],tot;
int ch[N*25][2],siz[N*25];

void insert(int&x,int i,int v,int t){
	if(!x) x=++tot;
	++siz[x];
	if(i==21) return;
	if(v>>i&1) insert(ch[x][1],i+1,v,t),++sum[t][i];
	else insert(ch[x][0],i+1,v,t);
}
void modify(int x,int i,int t){
	if(!x or i==21) return;
	sum[t][i]+=siz[ch[x][0]]-siz[ch[x][1]];
	swap(ch[x][0],ch[x][1]);
	modify(ch[x][0],i+1,t);
}
int merge(int x,int y){
	if(!x or !y) return x+y;
	siz[x]+=siz[y];
	ch[x][0]=merge(ch[x][0],ch[y][0]);
	ch[x][1]=merge(ch[x][1],ch[y][1]);
	return x;
}

int64 ans;

void dfs(int x){
	insert(root[x],0,a[x],x);
	for(int y:to[x]){
		dfs(y);
		modify(root[y],0,y);
		root[x]=merge(root[x],root[y]);
		for(int i=0;i<=20;++i) sum[x][i]+=sum[y][i];
	}
	for(int i=0;i<=20;++i)if(sum[x][i]&1) ans+=1<<i;
}
int main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=2;i<=n;++i) to[read<int>()].push_back(i);
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}

树上差分

https://www.luogu.com.cn/blog/dengyaotriangle/solution-p6623

看看就好。

CO int N=1<<21;
int a[N];
vector<int> to[N];
int w[21][N],f[N];

void dfs(int x,int d){
	f[x]=a[x];
	for(int i=0;i<=20;++i) f[x]^=w[i][d%(1<<i)];
	for(int y:to[x]) dfs(y,d+1),f[x]^=f[y];
	for(int i=0;i<=20;++i) f[x]^=w[i][d%(1<<i)];
	for(int i=0;i<=20;++i) w[i][(d+a[x])%(1<<i)]^=1<<i;
}
int main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=2;i<=n;++i) to[read<int>()].push_back(i);
	dfs(1,0);
	printf("%lld\n",accumulate(f+1,f+n+1,0LL));
	return 0;
}

posted on 2020-07-04 15:56  autoint  阅读(100)  评论(0编辑  收藏

导航