【状压DP】Atcoder XOR Tree

题意:

给出一棵树,每条边有边权 v v ,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值,求使得最终所有边权为0的最小操作次数。
v15


分析:

这里要用到一个特殊的技巧,我们将每个点的点权设为:其周围的所有边的边权的异或和。
这样一来,使得最终“所有边权为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);
                    else
                        dp[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));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值