1. 程式人生 > 實用技巧 >CF1464 F. My Beautiful Madness

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;
}