异或树
正题
链接到:https://www.luogu.com.cn/problem/AT3913
题目大意
给定一个有边权重的树,你可以选择一个链来一次异或所有有相同值的边,并寻找最少的操作数,这样所有边的权重都是\(0\)。
\(2\leq n\leq 10^5,0\leq w16\)
解题思路
一条边的权重可以看作是两个连通点权重的异或,那么我们就可以把这条边看作是点权重。
链的异或可以同时成为最后一个值的异或。
那么问题就变成了给出\(n\)个数字。您可以同时将两个数字与任意一个数字进行异或运算,并寻求最小运算次数,使它们都变成\(0\)。
那么很明显,我们直接异或最优的那个,现在最多只剩下\(16\)个不同的数字了。
考虑形状压力\(dp\),注意如果一个集合可以运算到\(0\),那么这个集合中所有数字的异或和必须是\(0\),因为无论序列如何运算,序列的异或和都不会改变。
那么从理论上讲,如果有\(x\)个数,那么我们运算次数的上限就是\(x-1\),但是如果我们能把集合\(S\)分成两个集合\(T,S-T\)并且这两个集合的异或和是\(0\),那么就变成\。
让\(f_S\)表示最多可以分成多少集\(S\),然后可以转移\ (o (3 {16}) \)。
复杂性:\ (o (n 3 {16}) \)
code
#includecstdio
# includecstring
#包括算法
使用命名空间标准;
const int N=1e5 10,M=16
int n,ans,a[N],v[M],c[1M],f[1M];
int main()
{
scanf('%d ',n);
for(int i=1,x,y,w;在;I){ 0
scanf('%d%d%d ',x,y,w);
a[x]^=w;a[y]^=w;
}
for(int I=0;在;[a[I]];
int MS=(116);
memset(f,0xcf,sizeof(f));
for(int s=1;短信;s)
for(int I=0;i16(一)
if((si)1){ 0
c[s]=c[s-(1i)]^i;
if(!c[s])f[s]=0;
打破;
}
int S=0;f[0]=0;
for(int I=1;i16(一)
ans=(v[i] 1)/2,S |=((v[I]1)I);
if(!s)返回printf('%d\n ',ans)0;
for(int s=1;短信;s){ 0
如果(c[s])继续;
for(int t=(s-1)s;t=(s^t);t=(t-1)s)
f[s]=max(f[s],f[t]f[s^t[1]);
}
printf('%d\n ',ans-f[S]-1);
返回0;
}
/*
四
0 1 5
1 2 3
2 3 5
*/
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/151779.html