CF1464 F. My Beautiful Madness
阿新 • • 發佈:2020-12-23
一棵樹,有個路徑集合。詢問\(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; }