给定一颗大小为\(n(n\le2*10^5)\)的树,\(m(m\le2*10^5)\)次操作,维护一个初始为空的路径集合\(P\)。
定义树上一条路径的\(d\)邻居(一个点集)\(S\)为:\(x \in S\)当前仅当存在一个路径上的点\(y\)满足\(dis(x,y)\le d\)。
操作分为三种:
\(1.\)输入\(u,v\),在\(P\)中加入\(u\)到\(v\)的路径。
\(2.\)输入\(u,v\),删除\(P\)中一个\(u\)到\(v\)的路径。
(注意\(u\)到\(v\)的路径与\(v\)到\(u\)的路径是相同的,若有多个\(u\)到\(v\)的路径只删除一个)
\(3.\)输入\(d\),询问\(P\)中所有路径的\(d\)邻居交集是否为空,若不为空输出Yes,否则输出No。
首先推敲一下这些点中有没有一个能被清晰表述出来。答案是有的,这个点是所有路径的 \(Lca\) 当中深度最深的那个点的 \(d\) 级祖先。所以我们干脆用一条路径的 \(Lca\) 来代替这一条路径。
证明很感性,设这个 \(Lca\) 为点 \(x\) ,如果有一条线段在比 \(p\) 还远的地方,那么它和 \(x\) 所代表的路径一定没有交点。
考虑树上差分,如果对于 \(p\) 和 \(p\) 的 \(d\) 级祖先 \(q\) ,树上路径 \(p\to q\) 被覆盖的次数不如路径总数,那么答案显然是 No 。因为存在有的路径到 \(p\) 的距离大于 \(d\) 。
再考虑 \(q\) 的子树直径。如果有两个 \(Lca\) 之间的距离大于 \(d\) ,那么这两条路径显然也不能相交。
这道题涉及的知识面有点广,我们来一一理一下:
首先要能求出树的 \(Lca\) 和 \(dfn\) 序;
利用multiset
来记录路径及其 \(Lca\) 的深度;
利用树状数组来维护树上差分。
利用线段树维护子树的直径。
这道题思维巧妙,涉猎面广,实现较复杂,不愧为 \(\mbox{CF}\) 3500
分的大佬题目。
#include<cstdio> #include<iostream> #include<set> using namespace std; namespace simpler{ template<class T>inline T smax(T fir){return fir;} template<class T>inline T smin(T fir){return fir;} template<class T,class ...Args>inline T smax(T fir,Args ...args){return max(fir,smax(args...));} template<class T,class ...Args>inline T smin(T fir,Args ...args){return min(fir,smin(args...));} inline bool isnum(char ch){return ch>='0'&&ch<='9';} template<class T=int> inline T read(){ T x(0);static char ch=getchar(); for(;!isnum(ch);ch=getchar()); for(; isnum(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48); return x; } template<class T> inline T& read(T& x){return x=read<T>();} template<class T,class ...Args> inline auto read(T& x,Args& ...args){return x=read<T>(),read(args...);} } using namespace simpler; const int Maxn=2e5+5; const int Inf=0x3f3f3f3f; class adjacency_list{ private: int nxt[Maxn<<1],val[Maxn<<1]; int h[Maxn]; int tot; public: inline void add(int x,int y){nxt[++tot]=h[x];val[tot]=y;h[x]=tot;} inline int head(int x){return h[x];} inline int next(int x){return nxt[x];} inline int operator[](int x){return val[x];} }atr; int L[Maxn],R[Maxn],dfn; int dep[Maxn]; int f[Maxn][22]; void dfs(int x,int fa){ L[x]=++dfn; f[x][0]=fa;dep[x]=dep[fa]+1; for(int i=1;i<=20;++i)f[x][i]=f[f[x][i-1]][i-1]; for(int i=atr.head(x);i;i=atr.next(i)) if(atr[i]!=fa)dfs(atr[i],x); R[x]=dfn; } inline int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=20;~i;--i) if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return x; for(int i=20;~i;--i) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } inline int dis(int x,int y){ if(x==-1||y==-1)return -Inf; int z=lca(x,y); return dep[x]+dep[y]-(dep[z]<<1); } struct node{ int x,y,len; node():x(-1),y(-1),len(-Inf){} node(int a,int b,int c):x(a),y(b),len(c){} friend bool operator<(node fir,node sec){return fir.len<sec.len;} friend node operator+(node fir,node sec){ if(fir.x==-1)return sec; if(sec.x==-1)return fir; node a=node(fir.x,sec.x,dis(fir.x,sec.x)); node b=node(fir.x,sec.y,dis(fir.x,sec.y)); node c=node(fir.y,sec.x,dis(fir.y,sec.x)); node d=node(fir.y,sec.y,dis(fir.y,sec.y)); return smax(fir,sec,a,b,c,d); } }; class segtree{ private: int l[Maxn<<2],r[Maxn<<2]; node val[Maxn<<2]; inline int ls(int x){return x<<1;} inline int rs(int x){return x<<1|1;} inline void pushup(int x){val[x]=val[ls(x)]+val[rs(x)];} public: void build(int L,int R,int x=1){ l[x]=L,r[x]=R; if(L==R)return ; int mid=L+R>>1; build(L,mid,ls(x)); build(mid+1,R,rs(x)); } void modify(int p,node d,int x=1){ if(l[x]==r[x]){val[x]=d;return ;} int mid=l[x]+r[x]>>1; modify(p,d,p<=mid?ls(x):rs(x)); pushup(x); } node query(int ql,int qr,int x=1){ if(ql<=l[x]&&r[x]<=qr)return val[x]; int mid=l[x]+r[x]>>1; if(qr<=mid)return query(ql,qr,ls(x)); if(ql>mid) return query(ql,qr,rs(x)); return query(ql,qr,ls(x))+query(ql,qr,rs(x)); } }seg; class tree_array{ private: int tr[Maxn]; inline int lowbit(int x){return x&-x;} public: inline void add(int x,int n,int d){ for(;x<=n;x+=lowbit(x))tr[x]+=d; } inline int ask(int x){ int res=0; for(;x;x-=lowbit(x))res+=tr[x]; return res; } inline int ask(int l,int r){return ask(r)-ask(l-1);} }c; multiset<pair<int,int>>s; int cnt[Maxn]; int getfa(int x,int k){ for(int i=0;i<=20;++i) if((k>>i)&1){ if(f[x][i])x=f[x][i]; else return 1; } return x; } int main(){ int n,m;read(n,m); for(int i=1;i<n;++i){ int x,y;read(x,y); atr.add(x,y); atr.add(y,x); } dfs(1,0);seg.build(1,n); while(m--){ int opt=read(); if(opt==1){ int x,y;read(x,y); int z=lca(x,y); c.add(L[x],n,1);c.add(L[y],n,1);c.add(L[z],n,-1); s.emplace(-dep[z],z); if(!cnt[z])seg.modify(L[z],node(z,z,0)); ++cnt[z]; }else if(opt==2){ int x,y;read(x,y); int z=lca(x,y); c.add(L[x],n,-1);c.add(L[y],n,-1);c.add(L[z],n,1); s.erase(s.find({-dep[z],z})); --cnt[z]; if(!cnt[z])seg.modify(L[z],node()); }else{ int d=read(); int x=getfa(s.begin()->second,d),y=getfa(x,d); if(c.ask(L[y],R[y])!=s.size()){puts("No");continue;} node res=seg.query(L[y],R[y]); puts(dis(x,res.x)<=d&&dis(x,res.y)<=d?"Yes":"No"); } } return 0; }