「CF1464F 」My Beautiful Madness 题解 (数据结构与树上算法)

题目简介

给定一颗大小为\(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;
}

$$-----EOF-----$$

posted @ 2022-07-26 19:57  AlienCollapsar  阅读(142)  评论(0)    收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq