Yoi #320. 撕破伤口
题面:
现在有一棵以1为根的树,树边有权值,每个点上有一个人,他们会互相阴阳怪气。
任意两个人互相阴阳怪气的程度定义为他们之间路径的异或和。
你想用撕破伤口、让他们互相更加阴阳怪气,所以你现在想知道对于每条树边,经过它的路径的最小阴阳怪气程度。
对于所有数据:1≤n≤105,0≤maxw<230,且树的形态随机
分析:
考虑快速求出路径的异或和,显然预处理出每个端点到根的路径的异或和,直接算,这是\(O(n^2)\)
考虑用trie树优化
发现树的形态随机,这意味着树高是期望\(O(lgn)\),
可以对整棵树建立一棵trie树,在trie树上每次删掉通过的树边的一个端点为根的子树,然后对每个子树中的点算一遍
由于每个点只会在其父亲被算的时候删一遍,算一遍,而父亲只有\(O(lgn)\),时间复杂度为\(O(nlgn)\)
#include<bits/stdc++.h>
const int INF=2e9;
using namespace std;
const int N=1e5+5,M=3e6+5;
int rt,cnt,n,f[N],ans,to[N],nxt[N],he[N],w[N],ch[M][2],num[M];
inline void add(int u,int v,int k) {
to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
w[cnt]=k;
}
void ins(int p,int x,int y) {
int u=1;
for(int i=29;i>=0;i--) {
int k=(x&(1<<i))!=0;
if(!ch[u][k]) ch[u][k]=++cnt;
u=ch[u][k];
num[u]+=y;
}
}
void dfs(int u) {
for(int e=he[u];e;e=nxt[e]) {
int v=to[e];
f[v]=f[u]^w[e];
dfs(v);
}
ins(rt,f[u],1);
}
vector<int>V;
void dgs(int u) {
ins(rt,f[u],-1),V.push_back(u);
for(int e=he[u];e;e=nxt[e]) {
int v=to[e];
dgs(v);
}
}
int ask(int p,int x) {
int u=p,sum=0; int ret=0;
for(int i=29;i>=0;i--) {
int k=(x&(1<<i))!=0;
if(num[ch[u][k]]) u=ch[u][k];
else u=ch[u][!k],ret|=(1<<i);
}
// printf("%d\n",ret);
return ret;
}
int main() {
freopen("wound.in","r",stdin);
freopen("wound.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n;i++) {
int u,v,k; scanf("%d%d%d",&u,&v,&k);
add(u,v,k);
}
rt=cnt=1; dfs(1);
// for(int i=1;i<=n;i++) {
// printf("%d\n",f[i]);
// }
for(int i=2;i<=n;i++) {
V.clear(),dgs(i);
ans=INF;
for(auto v:V){
ans=min(ans,ask(rt,f[v]));
}
for(auto v:V){
ins(rt,f[v],1);
}
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号