CF1464 F. My Beautiful Madness
一棵树,有个路径集合。询问\(d\),问是否存在点使得它到所有路径的距离(到路径上点的距离的最小值)不超过\(d\)。
支持:插入路径,删除路径,询问\(d\)。
\(n\le 2*10^5\)
结论:对于每个路径求\(LCA\),取深度最大的\(LCA\)的\(d\)级祖先(记为\(v\))。则如果存在交,那么\(v\)一定在交中。
证明考虑反证:如果存在交且\(v\)不在交中,考虑此时存在的一条路径,这条路径没有覆盖到\(v\)。此时,要么路径在\(v\)子树内,它的\(LCA\)的\(d\)级祖先深度一定大于\(dep_v\),矛盾;要么路径在\(v\)子树外且不覆盖\(v\),则此时它覆盖的地方一定不和生成\(v\)的路径覆盖的地方相交,矛盾。
于是询问的时候先找出\(v\)。现在只需要判定\(v\)是否合法。
可以求出\(v\)的\(d\)级祖先\(u\)。先判断是否所有路径都至少有一个端点在\(u\)的子树内,否则存在路径覆盖不到\(v\)。
然后取出每条路径的\(LCA\)挂在树上,在\(u\)的子树内找\(v\)的最远点。发现这个东西有时候可能是假的:存在路径和\((v,u)\)有交时。实际上这时候一定是合法的,而且通过这种方法判也不会错(\(LCA\)在\(u\)子树外不会被判到,\(LCA\)在\(u\)子树内时\(LCA\)一定在路径\((v,u)\)上)。
于是就是个子树内最远点的问题。
树链剖分或LCT解决。\(O(n\lg ^2 n)\)。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <cassert>
#define N 200005
#define INF 100000000
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
void link(int u,int v){
e[ne]={v,last[u]};
last[u]=e+ne++;
}
int fa[N],dep[N],siz[N],hs[N],top[N],ls[N],in[N],out[N],cnt;
void init1(int x){
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x]){
fa[ei->to]=x;
dep[ei->to]=dep[x]+1;
init1(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
}
}
void init2(int x,int t){
top[x]=t;
ls[++cnt]=x;
in[x]=cnt;
if (hs[x]){
init2(hs[x],t);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x] && ei->to!=hs[x])
init2(ei->to,ei->to);
}
out[x]=cnt;
}
int LCA(int u,int v){
while (top[u]!=top[v])
if (dep[top[u]]>dep[top[v]])
u=fa[top[u]];
else
v=fa[top[v]];
return dep[u]<dep[v]?u:v;
}
int kth(int x,int k){
if (k>=dep[x])
return 1;
while (dep[x]-dep[top[x]]<k){
k-=dep[x]-dep[top[x]]+1;
x=fa[top[x]];
}
return ls[in[x]-k];
}
struct TA{
int t[N];
void add(int x,int c){
for (;x<=n;x+=x&-x)
t[x]+=c;
}
int query(int x){
int r=0;
for (;x;x-=x&-x)
r+=t[x];
return r;
}
} ta;
void addta(int x,int c){
for (;x;x=fa[top[x]]){
ta.add(in[top[x]],c);
ta.add(in[x]+1,-c);
}
}
multiset<int,greater<int> > f[N],g[N];
struct SegTree{
int mx[N*4];
void build(int k=1,int l=1,int r=n){
mx[k]=-INF;
if (l==r)
return;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void change(int x,int c,int k=1,int l=1,int r=n){
if (l==r){
mx[k]=c;
return;
}
int mid=l+r>>1;
if (x<=mid) change(x,c,k<<1,l,mid);
else change(x,c,k<<1|1,mid+1,r);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
int query(int st,int en,int k,int l,int r){
if (st<=l && r<=en)
return mx[k];
int mid=l+r>>1,res=-INF;
if (st<=mid) res=query(st,en,k<<1,l,mid);
if (mid<en) res=max(res,query(st,en,k<<1|1,mid+1,r));
return res;
}
int query(int st,int en){
if (st>en)
return -INF;
return query(st,en,1,1,n);
}
} anc,sub;
int ex[N];
void build(){
for (int i=1;i<=n;++i){
g[i].insert(-INF);
f[top[i]].insert(-INF);
}
for (int i=1;i<=n;++i)
if (i!=1 && top[i]==i)
g[fa[i]].insert(-INF);
anc.build();
sub.build();
}
int mxdis(int u,int v){
int res=sub.query(in[v],out[v])-dep[v];
for (int x=v;dep[x]>=dep[u];x=fa[x]){
res=max(res,anc.query(max(in[top[x]],in[u]),in[fa[x]])+dep[v]);
x=top[x];
if (x!=1 && dep[x]-1>=dep[u]){
int y=fa[x];
auto p=g[y].begin();
if (*p==*f[x].begin())
++p;
res=max(res,*p+dep[v]-dep[y]*2);
res=max(res,sub.query(in[hs[y]],out[hs[y]])+dep[v]-2*dep[y]);
}
}
return res;
}
void modify(int w,int old,int now){
sub.change(in[w],now);
int pre=*g[w].begin();
g[w].erase(g[w].find(old));
g[w].insert(now);
if (pre==*g[w].begin())
return;
old=pre,now=*g[w].begin();
anc.change(in[w],now-dep[w]*2);
for (int x=w;x;x=fa[x]){
x=top[x];
int pre=*f[x].begin();
f[x].erase(f[x].find(old));
f[x].insert(now);
if (pre==*f[x].begin())
break;
old=pre,now=*f[x].begin();
if (x!=1){
int y=fa[x];
pre=*g[y].begin();
g[y].erase(g[y].find(old));
g[y].insert(now);
if (pre==*g[y].begin())
break;
old=pre,now=*g[y].begin();
anc.change(in[y],now-dep[y]*2);
}
}
}
void insert(int w){
if (ex[w]++)
return;
modify(w,-INF,dep[w]);
}
void erase(int w){
if (--ex[w])
return;
modify(w,dep[w],-INF);
}
struct cmps{bool operator()(int x,int y){return dep[x]>dep[y] || dep[x]==dep[y] && x<y;}};
multiset<int,cmps> s;
int have;
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v);
link(v,u);
}
init1(1);
init2(1,1);
dep[0]=-1;
build();
while (m--){
int op;
scanf("%d",&op);
if (op==3){
int d;
scanf("%d",&d);
int v=kth(*s.begin(),d),u=kth(v,d);
if (ta.query(in[u])!=have)
printf("No\n");
else if (mxdis(u,v)>d)
printf("No\n");
else
printf("Yes\n");
}
else{
int x,y;
scanf("%d%d",&x,&y);
int lca=LCA(x,y);
if (op==1){
have++;
s.insert(lca);
addta(x,1),addta(y,1),addta(lca,-1);
insert(lca);
}
else{
have--;
s.erase(s.find(lca));
addta(x,-1),addta(y,-1),addta(lca,1);
erase(lca);
}
}
}
return 0;
}