題解 世界樹的考驗
題目描述
具體做法與心路歷程
考試時的心情:懵逼.jpg
二分答案走一波,不行不行,邊權這麼小,這複雜度太底了。
那麼考慮?感無法滿足後效性。。
最後瞎寫了個的算法,喜提分。。
具體做法
我還是太菜了
又是一個小技巧。這種邊權異或的題目,我們往往可以把點權設爲其周圍所有邊的邊權的異或和。
在這道題中使所有邊權爲0
與現在使所有點權爲0
是等價的。
證明:考慮度數爲1的點,因爲其點權爲0,那麼其相鄰的那一條邊邊權也必然爲0,刪去這條邊與
這個點。持續這樣刪邊,使最終只剩一個點,得證。
這樣轉換成點權後每次操作即可看成修改兩個點的點權了:因爲對於路徑上的點而言,這條路徑要經過它相鄰的兩條邊,所以可以忽略。
那麼我們首先可以抵消掉點權相同的,最後還剩下一些點權互不相同的點。對剩下的點的權值我們可以用一個狀態來表示:的第位表示還有點權爲的點存在。設表示從狀態變成狀態的最小操作次數。即可。
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月05日 星期二 16時30分57秒
*******************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
const int maxn=1e5+10;
const int m=(1<<16)+10;
int n,f[m],sum[20],inf,ans,S,w[maxn];
int dfs(int S)
{
if(!S) return 0;
if(f[S]!=inf) return f[S];
for(int i=0;i<16;i++)
if((S>>i)&1)
{
for(int j=0;j<16;j++)
if(i!=j && ((S>>j)&1))
{
int p=i^j,x=S^(1<<i)^(1<<j)^(1<<p);
f[S]=min(f[S],dfs(x)+1+((S>>p)&1));
}
}
return f[S];
}
int main()
{
//freopen("trial.in","r",stdin);
//freopen("trial.out","w",stdout);
cin>>n;
int u,v,z;
for(int i=1;i<n;i++)
{
cin>>u>>v>>z;
w[u]^=z,w[v]^=z;
}
for(int i=0;i<n;i++) sum[w[i]]++;
for(int i=1;i<16;i++)
ans+=(sum[i]>>1),S+=(1<<i)*(sum[i]&1);
memset(f,127,sizeof f); inf=f[0];
printf("%d\n",ans+dfs(S));
return 0;
}