题意
给一棵
n
n
n个节点的树,边有边权。定义一条路径的权值为边权的异或和。找两条节点不相交的路径,使得这两条路径的权值和最大。
n
≤
30000
n\le 30000
n≤30000
分析
问题可以转化成对于每个点,求在该点的子树内和子树外分别找两个数,使得它们的异或的和尽可能大。
求出dfs序并倍增,就可以转化成在区间中选两个数,使得这两个数的异或和尽可能大。
用回滚莫队+Trie就好了。
回滚莫队具体来讲,就是左右端点都在同一个块内的询问直接暴力搞。把其他询问以左端点所在的块为第一关键字,右端点为第二关键字排序。在处理某个块内的询问的时候,记录
a
n
s
ans
ans表示该块的右端点到右指针的答案。每次把右指针推到询问的右端点,再把左指针从块的右端点推到询问的左端点,之后把左指针恢复就好了。
时间复杂度
O
(
n
n
log
V
)
O(n\sqrt n\log V)
O(nnlogV).
代码
#include<bits/stdc++.h>
#define pb push_back
const int N=30005;
int n,cnt,last[N],bel[N],tim,dfn[N],val[N],w[N*2],mx[N],sz,tot,B,bin[35],ans[N],rt;
struct edge{int to,next,w;}e[N*2];
struct data{int l,r,id;}q[N*2];
struct tree{int l,r,s;}t[N*30];
std::vector<int> vec[N];
void addedge(int u,int v,int w)
{
e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].w=w;e[cnt].next=last[v];last[v]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=++tim;
bel[tim]=x;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) val[e[i].to]=val[x]^e[i].w,dfs(e[i].to,x);
mx[x]=tim;
}
int cmp(int x,int y)
{
return q[x].r<q[y].r;
}
int get(int x)
{
return (x+B-1)/B;
}
void ins(int &x,int d,int w,int c)
{
if (!x) x=++sz;
t[x].s+=c;
if (d<0) return;
if (w&bin[d]) ins(t[x].r,d-1,w,c);
else ins(t[x].l,d-1,w,c);
}
int query(int x,int d,int w)
{
if (d<0) return 0;
if (w&bin[d])
if (t[t[x].l].s) return query(t[x].l,d-1,w)+bin[d];
else return query(t[x].r,d-1,w);
else
if (t[t[x].r].s) return query(t[x].r,d-1,w)+bin[d];
else return query(t[x].l,d-1,w);
}
int calc(int l,int r)
{
int mx=0;
for (int i=l;i<=r;i++) mx=std::max(mx,query(rt,30,w[i])),ins(rt,30,w[i],1);
for (int i=l;i<=r;i++) ins(rt,30,w[i],-1);
return mx;
}
void pre()
{
B=sqrt(n*2);
for (int i=1;i<=n;i++) w[i]=w[i+n]=val[bel[i]];
for (int i=2;i<=n;i++)
{
int l=dfn[i],r=mx[i],pos=get(l);
if (get(l)==get(r)) ans[i]+=calc(l,r);
else q[++tot].l=l,q[tot].r=r,q[tot].id=i,vec[pos].pb(tot);
l=mx[i]+1;r=dfn[i]+n-1;pos=get(l);
if (get(l)==get(r)) ans[i]+=calc(l,r);
else q[++tot].l=l,q[tot].r=r,q[tot].id=i,vec[pos].pb(tot);
}
for (int i=1;i<=get(n*2);i++) std::sort(vec[i].begin(),vec[i].end(),cmp);
}
void solve()
{
for (int i=1;i<=get(n*2);i++)
{
int r=B*i-1,mx=0;
for (int j=0;j<vec[i].size();j++)
{
int now=vec[i][j],tmp=0;
while (r<q[now].r) mx=std::max(mx,query(rt,30,w[++r])),ins(rt,30,w[r],1);
int l=B*i;
while (l>q[now].l) tmp=std::max(tmp,query(rt,30,w[--l])),ins(rt,30,w[l],1);
while (l<B*i) ins(rt,30,w[l++],-1);
ans[q[now].id]+=std::max(mx,tmp);
}
while (r>B*i-1) ins(rt,30,w[r--],-1);
}
}
int main()
{
bin[0]=1;
for (int i=1;i<=30;i++) bin[i]=bin[i-1]*2;
scanf("%d",&n);
for (int i=1;i<n;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
dfs(1,0);
pre();
solve();
int mx=0;
for (int i=2;i<=n;i++) mx=std::max(mx,ans[i]);
printf("%d\n",mx);
return 0;
}