其他分享
首页 > 其他分享> > Educational Codeforces Round 132 E,F

Educational Codeforces Round 132 E,F

作者:互联网

E

容易发现,我们在一个点进行修改,一定可以将经过这个点的非法路径数变成\(0\)。具体的方案就是将\(i\)异或上\(2^{p_{i}}\),其中\(p_{i}>30\),并且\(p\)要两两不同。(比如\(p_{i}=30+i\)就是一组可行的\(p\))

此时我们有一个贪心策略:为了让更多非法路径变成合法的,那么我们修改的点深度要尽可能小。

那么我们从叶子向根考虑:如果当前点\(p\),存在一个非法路径\((u,v)\),满足\(lca(u,v)=p\),那么我们一定修改\(p\),否则再往上就没有这条路径的点了,也无法让这条路径变成合法的了。

如何判断是否存在一个非法路径\((u,v)\),满足\(lca(u,v)=p\)呢?我们发现:设\(f(i)\)为从根到点\(i\)的路径的异或值,那么路径\((u,v)\)的权值异或和就等于\(f(u)\oplus f(v)\oplus a_{lca}\)。那么用\(S_{pos}\)存储当前子树的\(f\)的集合,在遇到一个儿子时,对于\(t\in S_{son}\),我们查询\(t\oplus a_{pos}\)是否在\(S_{pos}\)中出现过,如果有,那么意味着有一条异或和等于\(0\)的路径,其lca等于pos。最后再将\(S_{son}\)并入\(S_{pos}\)中即可。

注意修改\(p\)之后,我们不仅让\(lca=p\)的非法路径成为合法路径,同时还让所有经过\(p\)往根走的非法路径也变成合法的了。故如果我们要修改当前点\(p\),那么\(S_{p}\)需要清空。

具体代码如下:

void cnt(int pos,int fa=0) {
	int is=0;
	val[pos].insert(f[pos]);
	for(auto nxt : G[pos]) {
		if(nxt==fa) continue;
		cnt(nxt,pos);
		for(auto item : val[nxt]) {
			if(val[pos].find(item^a[pos])!=val[pos].end()) {
				is=1;
			}
		}
		val[pos].insert(val[nxt].begin(),val[nxt].end());
	}
	if(is) {
		ans++;
		val[pos].clear();
	}
}

但是这样时间复杂度有可能达到\(O(n^2\log n)\),因为将\(S_{son}\)并入\(S_{pos}\)中这个操作有可能是\(O(n \log n)\)的。如果我们采用启发式合并(将小的Set合并到大的Set中),那么时间复杂度将优化到\(O(n\log ^2 n)\)。

整体代码如下:

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

const int maxn=200005;
int n,ans;
int a[maxn],f[maxn];
std::set<int> val[maxn];
std::vector<int> G[maxn];

void dfs(int pos,int fa=0) {
	f[pos]=f[fa]^a[pos];
	for(auto nxt : G[pos]) {
		if(nxt==fa) continue;
		dfs(nxt,pos);
	}
}

void cnt(int pos,int fa=0) {
	int is=0;
	val[pos].insert(f[pos]);
	for(auto nxt : G[pos]) {
		if(nxt==fa) continue;
		cnt(nxt,pos);
		if(val[pos].size()<val[nxt].size()) {
			std::swap(val[pos],val[nxt]);
		}
		for(auto item : val[nxt]) {
			if(val[pos].find(item^a[pos])!=val[pos].end()) {
				is=1;
			}
		}
		val[pos].insert(val[nxt].begin(),val[nxt].end());
	}
	if(is) {
		ans++;
		val[pos].clear();
	}
}

int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n-1;i++) {
		int x,y; scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(1);	
	cnt(1);
	printf("%d\n",ans);
	return 0;
}

标签:nxt,Educational,val,fa,int,路径,Codeforces,pos,132
来源: https://www.cnblogs.com/Nastia/p/16511088.html