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