题解:AT_apc001_f XOR Tree
前言
好题,不过有点经典。
思路分析
首先一步树上差分将问题转化为,每次可以对一个点或两个个点异或,最小化所有点点权变为 \(0\) 的代价。
然后考虑一步操作至多将两个点权相等的点全部变为 \(0\),所以先把点权相等的配对消除一定不劣。
这样,每种点权只剩下 \(\le 1\) 个,然后设 \(f_s\) 表示点权集合 \(s\) 变为 \(0\) 的最少次数,枚举子集转移即可。
复杂度 \(O(n+3^v)\),注意 DP 数组的初始化。
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,x,y,w,tar,ans,a[100005];
int head[100005],nxt[200005],targetx[200005],targetw[200005],tot;
void add(int x,int y,int w){
tot++;
nxt[tot]=head[x];
head[x]=tot;
targetx[tot]=y;
targetw[tot]=w;
}
int f[2000005],g[2000005];
void dfs(int x,int fa){
for(int i=head[x];i;i=nxt[i]){
int y=targetx[i],w=targetw[i];
if(y==fa) continue;
a[y]=a[x]^w;
dfs(y,x);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
cin>>x>>y>>w;
x++,y++;
add(x,y,w);
add(y,x,w);
}
dfs(1,0);
for(int s=0;s<(1<<15);s++){
for(int i=1;i<=15;i++){
if(s&(1<<(i-1))) g[s]^=i;
}
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int s=0;s<(1<<15);s++){
if(g[s]) f[s]=min(f[s],__builtin_popcount(s));
else f[s]=min(f[s],__builtin_popcount(s)-1);
for(int t=(s-1)&s;t;t=(t-1)&s){
f[s]=min(f[s],f[t]+f[s^t]);
}
}
for(int i=1;i<=n;i++){
if(a[i]){
if(tar&(1<<(a[i]-1))) ans++;
tar^=(1<<(a[i]-1));
}
}
cout<<ans+f[tar];
return 0;
}

浙公网安备 33010602011771号