题解: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;
}
posted @ 2025-05-02 20:20  _Kenma  阅读(23)  评论(0)    收藏  举报