题目链接在这里
题意比较简单,就不赘述了。
我们直入主题:
首先我们按边权从小到大排序,这样我们每次取的边权一定是最大的限制的那一个。
然后我们需要在已经合并好的联通块(合并好的联通块里面的边权都是小于当前边权的,所以满足条件)里面找出异或和最大值。考虑使用01Trie树,我们在一颗Trie里面不断更新最大值,然后输出。并将树合并在一起。
由于数据范围较大,我们考虑启发式合并,我们在较大的Trie中不断更新较小的树的最大值,并且最后将小的子树合并到大的子树上。
复杂度(nlog2n)
下面是代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int p[maxn],sz[maxn];
int find(int x)
{
if(p[x]==x) return x;
return p[x] = find(p[x]);
}
int trie[maxn*32][2];
int root[maxn];
vector<int> e[maxn];
struct node
{
int from;
int to;
int cost;
bool operator<(const node&a)const
{
return cost<a.cost;
}
}G[maxn];
int n,tot = 0;
ll v[maxn];
void Insert(int rt,int x)
{
for(int i=18;i>=0;i--)
{
int id = (x>>i)&1;
if(!trie[rt][id]) trie[rt][id] = ++tot;
rt = trie[rt][id];
}
}
ll query(int rt,int x)
{
ll ans = 0;
for(int i=18;i>=0;i--)
{
int id = (x>>i)&1;
if(trie[rt][id^1])
{
ans |= (1<<i);
rt = trie[rt][id^1];
}
else rt = trie[rt][id];
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++) scanf("%d%d%d",&G[i].from,&G[i].to,&G[i].cost);
sort(G+1,G+n);
for(int i=1;i<=n;i++)
{
p[i] = i;
sz[i] = 1;
e[i].push_back(i);
root[i] = ++tot;
Insert(root[i],0);
}
for(int i=1;i<n;i++)
{
int x = G[i].from,y = G[i].to;
ll w = G[i].cost;
int fx = find(x),fy = find(y);
if(sz[fx]>sz[fy])
{
swap(x,y);
swap(fx,fy);
}
w ^= v[x]^v[y];
ll ans = 0;
for(int i=0;i<e[fx].size();i++)
{
ans = max(ans,query(root[fy],v[e[fx][i]]^w));
}
for(int i=0;i<e[fx].size();i++)
{
e[fy].push_back(e[fx][i]);
v[e[fx][i]] ^= w;
Insert(root[fy],v[e[fx][i]]);
}
p[fx] = fy;
sz[fy] += sz[fx];
printf("%d ",ans);
}
return 0;
}