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

2022/7/27 14:25:19

本文主要是介绍「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-----$$



这篇关于「CF1464F 」My Beautiful Madness 题解 (数据结构与树上算法)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程