CF1464 F. My Beautiful Madness

一棵树,有个路径集合。询问(d),问是否存在点使得它到所有路径的距离(到路径上点的距离的最小值)不超过(d)

支持:插入路径,删除路径,询问(d)

(nle 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(nlg ^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
");
			else if (mxdis(u,v)>d)
				printf("No
");
			else
				printf("Yes
");
		}
		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;
}
原文地址:https://www.cnblogs.com/jz-597/p/14181723.html