P6072 『MdOI R1』Path
问题
选择两条简单路径,满足没有重合的点,且边权异或和之和最大。
分析
首先我们考虑这个“没有重合的点”是个什么性质:
假设第一条路径的 \(lca\) 是 \(x\) ,那么一定是满足另外一条路径在 \(x\) 的子树外面(不在就交换,然后就在了)。
这是什么意思?
——也就是说,我们如果对这个树打上dfs序的话,我们相当于就是一段区间内的一条路径,和这段区间外的一条路径。(\(Trick:\)子树问题记得想一下\(dfs\)序,因为这样的话是连续的一段区间)
然后呢?
我们发现这样一个事情,相当于就是让我们在两个区间中分别选一些东西,然后加起来就是了。
但是我们发现这里的“一些东西”并不好直接维护,于是可以考虑转化:\(dis_u\) 表示 \(u\) 到根节点的边的异或和。(那么 \(u\) 到 \(v\) 的路径异或和其实就是两个点权异或一下就行了。)
但这里第二个区间是分散的,这怎么办?——我们可以把原来的序列复制一遍即可。
相信看到这里的话已经很明白了,如果把询问拆成两个,这就相当于直接让我们在一个区间当中选择两个点求异或最大值即可。
那么我们可以使用莫队来解决对于区间\(l,r\)的限制,然后就等价于维护全局最大值,异或最大值用 \(01trie\) 维护。
还有一个问题,那就是我们这里的异或最大值只是存起来的,不好删除,那么我们可以用回滚莫队来解决。
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=3e4+5,B=300,t=30,M=1e6+5,INF=1e9+7;
int n,head[N],to[N<<1],nex[N<<1],val[N<<1],idx=0;
void add(int u,int v,int w){
to[++idx]=v;
val[idx]=w;
nex[idx]=head[u];
head[u]=idx;
return;
}
int dis[N],dfn[N],ed[N],id[N],DFN=0;
void dfs(int x,int f){
dfn[x]=++DFN;id[DFN]=x;
for(int i=head[x];i;i=nex[i]){
int y=to[i],z=val[i];
if(y==f) continue;
dis[y]=dis[x]^z;
dfs(y,x);
}
ed[x]=DFN;
return ;
}
int Block,block,L[B],R[B],bl[N<<1];
int w[N<<1];
struct query{
int l,r,id;
bool operator <(const query &T){
if(bl[l]!=bl[T.l]) return l<T.l;
return r<T.r;
}
}Q[N<<1];
int ch[M][2],siz[M],ncnt=0;
void Insert(int x,int v){
int cur=0;
for(int i=t;~i;i--){
int d=x>>i&1;
if(!ch[cur][d]) ch[cur][d]=++ncnt;
cur=ch[cur][d];siz[cur]+=v;
}
}
int Query(int v){
int x=0,cur=0;
for(int i=t;~i;i--){
int d=v>>i&1;
if(siz[ch[cur][d^1]]) x|=1<<i,cur=ch[cur][d^1];
else cur=ch[cur][d];
}
return x;
}
int ans,res[N];
int main(){
read(n);
for(int i=1;i<n;i++){
int u,v,w;
read(u),read(v),read(w);
add(u,v,w);
add(v,u,w);
}
dfs(1,0);
for(int i=1;i<=n;i++) w[i]=dis[id[i]];
for(int i=n+1;i<=n*2;i++) w[i]=w[i-n];
Block=(int)sqrt(2*n);block=(2*n-1)/Block+1;
for(int i=1;i<=block;i++){
L[i]=(i-1)*Block+1;
R[i]=min(i*Block,2*n);
for(int j=L[i];j<=R[i];j++) bl[j]=i;
}
for(int i=2;i<=n;i++){
Q[i-1]=(query){dfn[i],ed[i],i};
Q[i+n-2]=(query){ed[i]+1,dfn[i]+n-1,i};
}
sort(Q+1,Q+(n<<1)-1);
int l=1,r=0;
for(int i=1;i<=(n<<1)-2;i++){
if(i==1||bl[Q[i].l]!=bl[Q[i-1].l]){
l=R[bl[Q[i].l]]+1;r=l-1;
memset(siz,0,sizeof(siz));
memset(ch,0,sizeof(ch));
ncnt=ans=0;
}
if(bl[Q[i].l]==bl[Q[i].r]){
int Max=-INF;
for(int j=Q[i].l;j<=Q[i].r;j++) Insert(w[j],1);
for(int j=Q[i].l;j<=Q[i].r;j++) Max=max(Max,Query(w[j]));
for(int j=Q[i].l;j<=Q[i].r;j++) Insert(w[j],-1);
res[Q[i].id]+=Max;
continue;
}
while(r<Q[i].r) Insert(w[++r],1),ans=max(ans,Query(w[r]));
int tmp=ans;
while(l>Q[i].l) Insert(w[--l],1),ans=max(ans,Query(w[l]));
res[Q[i].id]+=ans;
while(l<R[bl[Q[i].l]]+1) Insert(w[l++],-1);
ans=tmp;
}
int Max=0;
for(int i=1;i<=n;i++) Max=max(Max,res[i]);
write(Max);
return 0;
}