题意:
给出一棵树,每条边有边权vv,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值,求使得最终所有边权为0的最小操作次数。
分析:
这里要用到一个特殊的技巧,我们将每个点的点权设为:其周围的所有边的边权的异或和。
这样一来,使得最终“所有边权为0”与“所有点权为0”是等价的。
证明很简单,考虑每一个度数为1的点,因为其点权为0,那么其相邻的那一条边边权也必然为0,然后删去这条边与这个点。持续这样删边,使得最终只剩下一个点,证明就完成了。
这样做的好处是:我们可以将每次操作看为修改两个点的权值:因为对于路径上的点而言,这条路径要经过它相邻的两条边,根据异或的自反性,就可以忽略影响了。
那么很显然,权值相同的点直接异或,剩下的点的权值互不相同,状压DP查找当前状态到0的操作次数即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 0x3FFFFFFF
using namespace std;
int a[MAXN],w[MAXN],dp[MAXN];
int u,v,val;
int dfs(int s){if(s==0)return 0;if(dp[s])return dp[s];dp[s]=INF;for(int i=1;i<16;i++)if(s&(1<<i)){for(int j=1;j<16;j++){if(j!=i&&(s&(1<<j))){int q=i^j;if(s&(1<<q))dp[s]=min(dp[s],dfs(s^(1<<i)^(1<<j)^(1<<q))+2);elsedp[s]=min(dp[s],dfs(s^(1<<i)^(1<<j)^(1<<q))+1);}}}
}
int main(){int S,n,ans=0;SF("%d",&n);for(int i=1;i<n;i++){SF("%d%d%d",&u,&v,&val);a[u]^=val;a[v]^=val;}for(int i=0;i<n;i++)w[a[i]]++;for(int i=1;i<16;i++){ans+=w[i]/2;if(w[i]%2==1)S^=(1<<i);}PF("%d",ans+dfs(S));
}