2020 CCPC 长春站

F. Strange Memory

题目链接:https://codeforces.com/gym/102832/problem/F

\(dsu\ on\ tree\)
以当前节点 \(u\)\(lca\) 的点对就是 \(u\) 的不同子树内的点对,
同时我们可以发现: \(a[i] ^ a[j] = a[lca] -> a[i] ^ a[lca] = a[j]\),
于是问题就转化成了:对于一棵子树内的点 \(i\) ,点 \(i\) 的答案就是查询该子树外满足 $ a[i] ^ a[lca] = a[j]$ 的点 \(j\), \(i ^ j\) 的值之和
考虑 \(dsu\ on\ tree\),先统计轻儿子的答案,统计完后消去影响,再统计重儿子,统计完后保留,

查询的时候需要的办法没有想到:
根据异或的性质:只需要开一个桶 \(c[a][i][0/1]\), 表示异或值为 \(a\) 的所以点中,点的编号的第 \(i\) 位为 \(0/1\) 的点的数量
因为只有同一位的数字不同才会产生答案,所以点 \(i\) 的答案就是 \(c[a[i] ^ a[lca]][x][((i >> x) & 1) ^ 1] * p[x]\), 其中 \(p[x] = 1 << x\),即该位的权值

因为统计当前子树答案时,重儿子的值已经处理好了,所以只需要依次处理轻儿子,先查询当前轻儿子内的点与先前统计过的子树中的点对的答案,
再将当前子树的值加入桶中,这样就会保证路径不重不漏

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 100010;

int n;

int h[maxn], cnt = 0;
struct E{
	int to, next;
}e[maxn << 1];
void add(int u, int v){
	e[++cnt].next = h[u];
	e[cnt].to = v;
	h[u] = cnt;
}

ll ans = 0;
int c[1200000][22][2], a[maxn], p[30]; // 桶 
int mx, Son, sz[maxn], son[maxn];

void dfs1(int u, int par){
	sz[u] = 1;
	int mx = 0;
	for(int i = h[u] ; i != -1; i = e[i].next){
		int v = e[i].to;
		if(v == par) continue;
		dfs1(v, u);
		if(sz[v] > mx){
			mx = sz[v];
			son[u] = v;
		} 
		sz[u] += sz[v]; 
	}
}

void calc(int u, int par, int x){
	for(int i = 21 ; i >= 0 ; --i){
		int num = (u >> i) & 1;
		ans += 1ll * c[a[u] ^ x][i][num ^ 1] * p[i];
	}
	
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(v == par) continue;
		calc(v, u, x);
	}
}

void update(int u, int par, int val){ 
	for(int i = 21 ; i >= 0 ; --i){
		int num = (u >> i) & 1;
		c[a[u]][i][num] += val;
	}
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to; 
		if(v == par || v == Son) continue;
		update(v, u, val);
	} 
}

void dfs2(int u, int par, int is){
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(v == par || v == son[u]) continue;
		dfs2(v, u, 0);
	}
 
	if(son[u]) dfs2(son[u], u, 1); 
	
	for(int i = 21 ; i >= 0 ; --i){
		int num = (u >> i) & 1;
		c[a[u]][i][num] += 1;
	}	
	for(int i = h[u] ; i != -1 ; i = e[i].next){
		int v = e[i].to;
		if(v == par || v == son[u]) continue;
		calc(v, u, a[u]);
		update(v, u, 1);
	} 

	if(!is) update(u, par, -1);
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	memset(h, -1, sizeof(h));
	p[0] = 1;
	for(int i = 1 ; i <= 22 ; ++i) p[i] = p[i - 1] * 2;
	n = read(); 
	for(int i = 1 ; i <= n ; ++i){
		a[i] = read(); 
	}
	
	int u, v;
	for(int i = 1 ; i < n ; ++i){
		u = read(), v = read();
		add(u, v), add(v, u);
	}
	
	dfs1(1, 0);
	
	dfs2(1, 0, 1);
	
	printf("%lld\n", ans);
	
	return 0;
}
posted @ 2020-12-03 21:19  Tartarus_li  阅读(101)  评论(0编辑  收藏  举报