题解:P17553 [JAG 2026 Summer Camp #2] Edge Exchanges
concertb
·
·
题解
UPD:这个超级菜的蒟蒻把复杂度算错了。。。关联工单
DSU 套树剖。
对于 A 中一条边 (u,p),其中 p 是 u 的父亲,断开变成两个连通块,其中一个是以 u 为根的子树。
在 B 中找到 p 到 u 的路径,则合法的可交换的边 (x,y) 一定满足在这条 p 到 u 的路径上,且 x 在 A 中在以 u 为根的子树(以下简称被标记),y 没有被标记。
从 p 开始向 u 走,遇到的第一个被标记的点的前驱一定没被标记(废话,因为 p 一定不被标记),这一定是合法的边。
于是我们需要维护在 B 上的某一条链中,最小和最大的被标记的点的深度,并支持增加和撤销标记,树剖加线段树即可。
:::info[Code]
```cpp
#include<bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
const int N=5e5+5;
const int inf=1e9;
int n;
array<vector<pair<int,int> >,N> g,e;
array<int,N> ida,idb,ans;
namespace TrB{
int tot;
array<int,N> fa,dep,sz,son,tp;
array<int,N> in,out,rk;
void dfs1(int u){
sz[u]=1;
for(auto [v,id]:e[u]){
if(v==fa[u]) continue;
fa[v]=u;
idb[v]=id;
dep[v]=dep[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v])
son[u]=v;
}
return;
}
void dfs2(int u,int top){
in[u]=++tot;
rk[tot]=u;
tp[u]=top;
if(son[u]) dfs2(son[u],top);
for(auto [v,id]:e[u]){
if(v==fa[u]) continue;
if(v==son[u]) continue;
dfs2(v,v);
}
out[u]=tot;
return;
}
int lca(int u,int v){
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]]) swap(u,v);
u=fa[tp[u]];
}
return dep[u]<dep[v]?u:v;
}
int up(int u,int k){
while(k<=dep[fa[tp[u]]])
u=fa[tp[u]];
return rk[in[u]-(dep[u]-k)];
}
array<int,N<<2> mn,mx;
void push_up(int p){
mn[p]=min(mn[p<<1],mn[p<<1|1]);
mx[p]=max(mx[p<<1],mx[p<<1|1]);
return;
}
void upd(int p,int l,int r,int x,int f){
if(r<x||l>x) return;
if(l==r){
if(f==1) mn[p]=mx[p]=dep[rk[x]];
if(f==-1) mn[p]=inf,mx[p]=-inf;
return;
}
int mid=l+r>>1;
upd(p<<1,l,mid,x,f);
upd(p<<1|1,mid+1,r,x,f);
push_up(p);
return;
}
int qry_mn(int p,int l,int r,int x,int y){
if(r<x||l>y) return inf;
if(x<=l&&r<=y) return mn[p];
int mid=l+r>>1;
return min(qry_mn(p<<1,l,mid,x,y),qry_mn(p<<1|1,mid+1,r,x,y));
}
int qry_mx(int p,int l,int r,int x,int y){
if(r<x||l>y) return -inf;
if(x<=l&&r<=y) return mx[p];
int mid=l+r>>1;
return max(qry_mx(p<<1,l,mid,x,y),qry_mx(p<<1|1,mid+1,r,x,y));
}
int query(int u,int v){
int A=u,B=v,l=lca(u,v);
int res=-inf;
if(v!=l){
v=fa[v];
while(tp[v]!=tp[l]){
res=max(res,qry_mx(1,1,n,in[tp[v]],in[v]));
v=fa[tp[v]];
}
res=max(res,qry_mx(1,1,n,in[l],in[v]));
}
else l=up(u,dep[l]+1);
if(res!=-inf) return up(B,res+1);
res=inf;
while(tp[u]!=tp[l]){
res=min(res,qry_mn(1,1,n,in[tp[u]],in[u]));
u=fa[tp[u]];
}
res=min(res,qry_mn(1,1,n,in[l],in[u]));
assert(res!=inf);
return up(A,res);
}
}
namespace TrA{
int tot;
array<int,N> fa,dep,sz,son;
array<int,N> in,out,rk;
void dfs1(int u){
in[u]=++tot;
rk[tot]=u;
sz[u]=1;
for(auto [v,id]:g[u]){
if(v==fa[u]) continue;
fa[v]=u;
ida[v]=id;
dep[v]=dep[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v])
son[u]=v;
}
out[u]=tot;
return;
}
void dfs2(int u,int keep){
for(auto [v,id]:g[u]){
if(v==fa[u]) continue;
if(v==son[u]) continue;
dfs2(v,0);
}
if(son[u]) dfs2(son[u],1);
for(auto [v,id]:g[u]){
if(v==fa[u]) continue;
if(v==son[u]) continue;
for(int i=in[v];i<=out[v];i++)
TrB::upd(1,1,n,TrB::in[rk[i]],1);
}
TrB::upd(1,1,n,TrB::in[u],1);
if(u!=1)
ans[ida[u]]=idb[TrB::query(u,fa[u])];
if(!keep){
for(int i=in[u];i<=out[u];i++)
TrB::upd(1,1,n,TrB::in[rk[i]],-1);
}
return;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("exchanges6.in","r",stdin);
freopen("out.out","w",stdout);
clock_t Start=clock();
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(make_pair(v,i));
g[v].push_back(make_pair(u,i));
}
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(make_pair(v,i));
e[v].push_back(make_pair(u,i));
}
TrB::mn.fill(inf);
TrB::mx.fill(-inf);
TrA::dep[1]=TrB::dep[1]=1;
TrB::dfs1(1);
TrB::dfs2(1,1);
TrA::dfs1(1);
TrA::dfs2(1,0);
for(int i=1;i<n;i++)
cout<<ans[i]<<endl;
cerr<<(double)(clock()-Start)/CLOCKS_PER_SEC<<endl;
return 0;
}
```
:::