CF1464F My Beautiful Madness解题报告:
题意
给定一个 (n) 个结点的树,你需要维护一个路径的可重集。一共会进行 (m) 次操作,每次支持加入/删除一条路径,或者是查询可重集内所有路径的 (d) 邻域是否有交。
(1leqslant nleqslant 2 imes 10^5,2leqslant mleqslant 2 imes 10^5)。
分析
性质题。
直接求交信息过于复杂,考虑找到一个点满足其包含在交内的概率必然最高。
我们发现这个点是所有路径的 ( ext{lca}) 最深点的 (d) 级祖先(设其为 (p)),如果 (p) 不在某条路径的 (d) 邻域中的话,那么 ( ext{lca}) 最深的路径的 (d) 邻域一定与这条路径无交。
于是我们用一个 ( ext{multiset}) 维护一下这个位置,考虑怎么判断它是否在交中。
我们取出 (p) 的 (d) 级祖先 (q),那么在 (q) 子树外的路径一定不包含 (p),我们用一个树状数组维护一下树上差分就可以了。
在 (q) 内的路径最靠近 (p) 的位置一定是它的 ( ext{lca}),于是我们维护一下子树内当前所有关键点的直径就好了。
时间复杂度 (O((n+q)log n))。
代码
注意细节,实际上代码还是比较容易实现的。
#include<stdio.h>
#include<vector>
#include<set>
#define lc(x) (x)<<1
#define rc(x) (x)<<1|1
#define lowbit(x) x&(-x)
#define inf 1000000000
using namespace std;
const int maxn=200005,maxt=maxn<<2,maxk=21;
int n,m,euls,dfns;
int st[maxn<<1][maxk],eul[maxn],lg[maxn<<1],dep[maxn],L[maxn],R[maxn],tt[maxn],fore[maxn][maxk],cnt[maxn];
vector<int>v[maxn];
multiset< pair<int,int> >s;
void dfs(int x,int last){
eul[x]=++euls,st[euls][0]=x,L[x]=++dfns;
dep[x]=dep[last]+1,fore[x][0]=last;
for(int i=1;i<=20;i++)
fore[x][i]=fore[fore[x][i-1]][i-1];
for(int i=0;i<v[x].size();i++)
if(v[x][i]!=last)
dfs(v[x][i],x),st[++euls][0]=x;
R[x]=dfns;
}
inline int calc(int a,int b){
return dep[a]<dep[b]? a:b;
}
inline int lca(int a,int b){
a=eul[a],b=eul[b];
if(a>b)
swap(a,b);
int k=lg[b-a+1];
return calc(st[a][k],st[b-(1<<k)+1][k]);
}
inline int dis(int a,int b){
if(a==-1||b==-1)
return -inf;
int c=lca(a,b);
return dep[a]+dep[b]-2*dep[c];
}
int getkth(int x,int k){
for(int i=0;i<=20&&x!=1;i++)
if((k>>i)&1)
x=fore[x][i];
return x;
}
void tupdate(int x,int v){
for(int i=x;i<=n;i+=lowbit(i))
tt[i]+=v;
}
int tquery(int x){
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=tt[i];
return res;
}
struct node{
int x,y,len;
}t[maxt];
node merge(node p,node q){
if(p.x==-1)
return q;
if(q.x==-1)
return p;
node res=p.len>q.len? p:q;
int a=dis(p.x,q.x),b=dis(p.x,q.y),c=dis(p.y,q.x),d=dis(p.y,q.y);
if(a>res.len)
res=node{p.x,q.x,a};
if(b>res.len)
res=node{p.x,q.y,b};
if(c>res.len)
res=node{p.y,q.x,c};
if(d>res.len)
res=node{p.y,q.y,d};
return res;
}
inline void pushup(int now){
t[now]=merge(t[lc(now)],t[rc(now)]);
}
void build(int l,int r,int now){
t[now]=node{-1,-1,-inf};
if(l==r)
return ;
int mid=(l+r)>>1;
build(l,mid,lc(now)),build(mid+1,r,rc(now));
}
void modify(int l,int r,int now,int p,int x,int w){
if(l==r){
if(w==1)
t[now]=node{x,x,0};
if(w==0)
t[now]=node{-1,-1,-inf};
return ;
}
int mid=(l+r)>>1;
if(p<=mid)
modify(l,mid,lc(now),p,x,w);
else modify(mid+1,r,rc(now),p,x,w);
pushup(now);
}
node query(int l,int r,int now,int L,int R){
if(L<=l&&r<=R)
return t[now];
int mid=(l+r)>>1;
if(L<=mid&&mid<R)
return merge(query(l,mid,lc(now),L,R),query(mid+1,r,rc(now),L,R));
if(L<=mid)
return query(l,mid,lc(now),L,R);
return query(mid+1,r,rc(now),L,R);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y),v[y].push_back(x);
}
dfs(1,1);
lg[0]=-1;
for(int i=1;i<=euls;i++)
lg[i]=lg[i>>1]+1;
for(int i=1;i<=20;i++)
for(int j=1;j+(1<<i)-1<=euls;j++)
st[j][i]=calc(st[j][i-1],st[j+(1<<(i-1))][i-1]);
build(1,n,1);
while(m--){
int t,x,y,z;
scanf("%d%d",&t,&x);
if(t==1){
scanf("%d",&y),z=lca(x,y);
tupdate(L[x],1),tupdate(L[y],1),tupdate(L[z],-1);
s.insert(make_pair(dep[z],z));
if(cnt[z]==0)
modify(1,n,1,L[z],z,1);
cnt[z]++;
}
if(t==2){
scanf("%d",&y),z=lca(x,y);
tupdate(L[x],-1),tupdate(L[y],-1),tupdate(L[z],1);
s.erase(s.lower_bound(make_pair(dep[z],z)));
cnt[z]--;
if(cnt[z]==0)
modify(1,n,1,L[z],z,0);
}
if(t==3){
y=getkth(s.rbegin()->second,x),z=getkth(y,x);
if(tquery(R[z])-tquery(L[z]-1)!=s.size()){
puts("No");
continue;
}
node res=query(1,n,1,L[z],R[z]);
puts((dis(y,res.x)<=x&&dis(y,res.y)<=x)? "Yes":"No");
}
}
return 0;
}