Codeforces 1464F - My Beautiful Madness(树的直径)

Codeforces 题面传送门 & 洛谷题面传送门

树上数据结构大杂烩(?)

首先考虑什么样的点能够在所有路径的 \(d\) 邻居的交集内。显然如果一个点在一条路径的 \(d\) 邻居内则必须有该点到这条路径上所有点中最近的点的距离 \(\le d\),因此一个点在所有路径 \(d\) 邻居的交集内,当且仅当对于所有路径,该点到该路径上点距离的最小值的最大值 \(\le d\),我们即需判定是否存在这样的点。

直接维护显然不容易,不过我们思考这样一个问题:是否存在一个点,满足只要所有路径的 \(d\) 邻居的交集非空,该点就必然在这些路径 \(d\) 邻居的交集内?答案是肯定的,我们考虑以 \(1\) 为根对整棵树进行一遍 DFS,那么所有路径的两个端点的 LCA 中,深度最大的那个的 \(d\)​ 级祖先即为符合要求的点。具体证明大概就,设 \(u\) 为深度最大的祖先的 \(d\) 级祖先,那么:

  • 对于两个端点都在 \(u\) 子树内的路径,由于 \(u\) 到这样的路径中任意一点距离的最小值为 \(u\) 到路径两端点的 LCA 的距离,而 \(u\) 到 LCA 最深的路径的距离不过 \(d\),因此对于这样的路径一定是符合条件的,同时说明不在 \(u\) 子树内的点一定不符合要求,因为它到 LCA 深度最大的路径的最小距离肯定 \(>d\)
  • 对于一个端点在 \(u\) 子树内的点,另一个端点不在的路径,显然这样的路径会经过 \(u\),自然符合条件
  • 对于两个端点都不在 \(u\) 子树内的点,由于所有符合要求的点都在 \(u\) 子树内,所以 \(u\)\(u\) 子树内到这样的路径距离最小的点,也就是说 \(u\) 是最有可能符合要求的点。

显然这个 \(u\) 可以通过 set 之类的东西维护,于是现在问题转化为如何判定一个点是否到所有路径距离都 \(\le d\)

首先考虑 \(u\)\(d\) 级祖先 \(v\),如果存在一个路径满足其与 \(v\) 的子树无交集,那么显然 \(u\) 到这样的路径距离 \(>d\),也就不符合要求,这个可以通过加入一条路径 \((u,v)\) 时对 \(1\to u\) 路径上所有点 \(+1\)\(1\to v\) 路径上所有点 \(+1\)\(1\to\text{LCA}(u,v)\) 路径上所有点都 \(-1\),然后判断 \(v\) 的权值是否等于当前路径条数即可。其次还有一个必要条件就是 \(u\) 到所有 \(\text{LCA}\)\(v\) 子树内的点的距离的 \(\text{LCA}\le d\)(当然如果路径的两个端点一个在 \(u\) 子树内,一个不在 \(u\) 子树内,那么 \(u\) 到这样的路径的最小距离并不是 \(u\) 到它们 LCA 的距离,而是 \(0\),不过由于显然 \(u\) 到它们 LCA 的距离 \(\le d\),因此这个转化并不影响)根据直径那套理论,如果我们称 \(v\) 子树内一个点为关键点,当且仅当它是某条路径的 LCA,那么 \(v\) 子树内距离 \(u\) 最远的关键点就是 \(u\) 子树内所有关键点组成的直径之一,这个可以通过 DFS 序+树的直径的合并方式维护。不难发现一个点 \(u\) 符合条件的充要条件就这么多,于是这题就做完了。

时间复杂度 \((n+q)\log n\)

orz 线段树分治解法……

const int MAXN=2e5;
const int LOG_N=19;
int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dfn_eu[MAXN+5],tim_eu=0,dep[MAXN+5],dfn[MAXN+5],ed[MAXN+5],tim=0;
int fa[MAXN+5][LOG_N+2];
pii st[MAXN*2+5][LOG_N+2];
void dfs(int x,int f){
	fa[x][0]=f;dfn[x]=++tim;
	st[dfn_eu[x]=++tim_eu][0]=mp(dep[x],x);
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f) continue;
		dep[y]=dep[x]+1;dfs(y,x);
		st[dfn_eu[x]=++tim_eu][0]=mp(dep[x],x);
	} ed[x]=tim;
}
pii query_st(int l,int r){
	int k=31-__builtin_clz(r-l+1);
	return min(st[l][k],st[r-(1<<k)+1][k]);
}
int getlca(int x,int y){
	x=dfn_eu[x];y=dfn_eu[y];if(x>y) swap(x,y);
	return query_st(x,y).se;
}
int get_kanc(int x,int k){
	for(int i=LOG_N;~i;i--) if(k>>i&1) x=fa[x][i];
	return max(x,1);
}
int getdist(int x,int y){return dep[x]+dep[y]-(dep[getlca(x,y)]<<1);}
void st_init(){
	dfs(1,0);
	for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n*2;j++)
		st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
	for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
}
struct fenwick_tree{
	int t[MAXN+5];
	void add(int x,int v){for(int i=x;i<=n;i+=(i&(-i))) t[i]+=v;}
	int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
	int query_range(int l,int r){return query(r)-query(l-1);}
} t;
int cnt_pth=0;
int qry_sub(int x){return t.query_range(dfn[x],ed[x]);}
void add_pth(int u,int v,int x){
	t.add(dfn[u],x);t.add(dfn[v],x);
	t.add(dfn[getlca(u,v)],-x);
}
multiset<pii> lca;
struct dat{
	int u,v;
	dat(int _u=0,int _v=0):u(_u),v(_v){}
	dat operator +(const dat &rhs){
		if(!u) return rhs;
		if(!rhs.u) return *this;
		vector<pair<int,pii> > dists;
		dists.pb(mp(getdist(u,rhs.u),mp(u,rhs.u)));
		dists.pb(mp(getdist(u,rhs.v),mp(u,rhs.v)));
		dists.pb(mp(getdist(v,rhs.u),mp(v,rhs.u)));
		dists.pb(mp(getdist(v,rhs.v),mp(v,rhs.v)));
		dists.pb(mp(getdist(u,v),mp(u,v)));
		dists.pb(mp(getdist(rhs.u,rhs.v),mp(rhs.u,rhs.v)));
		sort(dists.begin(),dists.end());
		return dat(dists[5].se.fi,dists[5].se.se);
	}
};
struct node{int l,r;dat v;} s[MAXN*4+5];
void pushup(int k){s[k].v=s[k<<1].v+s[k<<1|1].v;}
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;if(l==r) return;int mid=l+r>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,dat v){
	if(s[k].l==s[k].r) return s[k].v=v,void();
	int mid=s[k].l+s[k].r>>1;
	(p<=mid)?modify(k<<1,p,v):modify(k<<1|1,p,v);
	pushup(k);
}
dat query(int k,int l,int r){
	if(l<=s[k].l&&s[k].r<=r) return s[k].v;
	int mid=s[k].l+s[k].r>>1;
	if(r<=mid) return query(k<<1,l,r);
	else if(l>mid) return query(k<<1|1,l,r);
	else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}
int cnt[MAXN+5],pth_cnt=0;
void ins_pth(int u,int v){
	pth_cnt++;add_pth(u,v,1);int lc=getlca(u,v);
	cnt[lc]++;lca.insert(mp(dep[lc],lc));
	if(cnt[lc]==1) modify(1,dfn[lc],dat(lc,lc));
}
void del_pth(int u,int v){
	pth_cnt--;add_pth(u,v,-1);int lc=getlca(u,v);
	cnt[lc]--;lca.erase(lca.find(mp(dep[lc],lc)));
	if(cnt[lc]==0) modify(1,dfn[lc],dat(0,0));
}
bool check(int d){
	pii pp=*lca.rbegin();
	int u=get_kanc(pp.se,d),v=get_kanc(u,d);
	if(qry_sub(v)!=pth_cnt) return 0;
	dat dt=query(1,dfn[v],ed[v]);
	if(max(getdist(u,dt.u),getdist(u,dt.v))>d) return 0;
	return 1;
}
int main(){
	scanf("%d%d",&n,&qu);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
	st_init();build(1,1,n);
	while(qu--){
		int opt;scanf("%d",&opt);
		if(opt==1){
			int u,v;scanf("%d%d",&u,&v);
			ins_pth(u,v);
		} else if(opt==2){
			int u,v;scanf("%d%d",&u,&v);
			del_pth(u,v);
		} else {
			int d;scanf("%d",&d);
			printf("%s\n",(check(d))?"Yes":"No");
		}
	}
	return 0;
}
posted @ 2021-09-06 12:00  tzc_wk  阅读(59)  评论(1)    收藏  举报