P5157 [USACO18DEC] The Cow Gathering P 题解

P5157 [USACO18DEC] The Cow Gathering P 题解

一种比正解复杂的但是比较好想的做法。

我们先简化一下题意,对于每一个限制对 \((a,b)\),我们令 \(b\) 为根结点,那么 \(a\) 的子树内的点都无法最后被删去,(因为 \(a\) 无法最后被删去,删了 \(a\) 就断了)。

我们将这些点与 \(b\) 暴力连边,那么出度为 \(0\) 的点就可以最后选。

当然,还要判断无解情况:如果这些边构成了一个环,那么本身无论如何都删不完,所以所有点都无解。

因此我们有了 \(O(n^2)\) 的暴力做法,可以得到 20 分。

考虑优化,发现换根的过程增大了不必要的时间开销,尝试省掉这个过程。

我们钦定 \(1\) 为根结点,那么对于限制对 \((a,b)\),有如下两种情况:

  1. \(b\)\(a\) 的子树之外。

这种情况下,换成以 \(b\) 为根的时候,\(a\) 的子树是不变的。

  1. \(b\)\(a\) 的子树之内。

这种情况子树就会变化。

如这一张图,我们换到 \(b\) 为根时,\(a\) 的子树包含的点是 \(\{a,4,9,5,2,1,3\}\)。也就是说,我们要把 \(a\) 的子树中 \(b\) 所在的子树删掉。如果我们对原树做一个 dfn 序,新的子树会被分成两连续部分。

考虑寻找 \(b\) 所在子树对应的 \(a\) 的儿子 \(v\),我们相当于要把 \(v\) 所在的子树删去。

这个寻找的过程显然可以用树链剖分进行维护,我们从 \(b\) 开始暴力跳链,如果 \(a\)\(b\) 链顶不一样就跳到链顶,如果已经满足 \(fa[b]=a\) 就结束,否则上跳到下一条链继续。

若已经跳到在同一个重链上了,那么 \(v\) 就是 \(son[u]\)

我们对于一段 dfn 连续的区间的出度 \(+1\),可以用差分实现,但是还要找到环判断无解,因此还是需要线段树优化建图,跑一个 tarjan 找环。

时间复杂度 \(O(n \log n)\)


#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int f[500010],dep[500010],sz[500010],dfn[500010],son[500010],top[500010],idx;
int oedge[500010];
vector<int> nG[2000010],g[500010];
void dfs(int u,int fa){
	f[u]=fa,dep[u]=dep[fa]+1,sz[u]=1;
	for(auto v:g[u]){
		if(v^fa){
			dfs(v,u);
			son[u]=sz[v]>sz[son[u]]?v:son[u];
			sz[u]+=sz[v];
		}
	}
}
void dfs2(int u,int topnode){
	top[u]=topnode,dfn[u]=++idx;
	if(son[u]) dfs2(son[u],topnode);
	for(auto v:g[u]){
		if((v^son[u])&&(v^f[u])) dfs2(v,v);
	}
}
int dfn_tar[2000010],n_tar,scc,low[2000010],idx_tar,gid[2000010],fid[500010],n,m,ons[2000010];
int st[2000010],top_tar;
void build(int id,int l,int r){
	gid[id]=++n_tar;
	if(l==r){
		fid[l]=gid[id];
		return;
	}
	int mid=(l+r)>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	nG[gid[id<<1]].push_back(gid[id]);
	nG[gid[id<<1|1]].push_back(gid[id]);
}
void update(int id,int l,int r,int ql,int qr,int v){
	if(ql<=l&&r<=qr){
		nG[gid[id]].push_back(fid[v]);
		return;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) update(id<<1,l,mid,ql,qr,v);
	if(qr>mid) update(id<<1|1,mid+1,r,ql,qr,v);
}
void tarjan(int u){
	low[u]=dfn_tar[u]=++idx_tar;
	ons[u]=1;
	st[++top_tar]=u;
	for(auto v:nG[u]){
		if(!dfn_tar[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(ons[v]) low[u]=min(low[u],dfn_tar[v]);
	}
	if(low[u]==dfn_tar[u]){
		scc++;
		while(st[top_tar]!=u){
			ons[st[top_tar]]=0;
			top_tar--;
		}
		ons[u]=0;
		top_tar--;
	}
}
int main(){

	n=read(),m=read();
	for(int i=2;i<=n;i++){
		int u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1,0);
	dfs2(1,1);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		int vp=v;
		if(dfn[v]<dfn[u]||dfn[v]>dfn[u]+sz[u]-1){
			update(1,1,n,dfn[u],dfn[u]+sz[u]-1,dfn[vp]);
			oedge[dfn[u]]++,oedge[dfn[u]+sz[u]]--;
			continue;
		}
		while(top[v]!=top[u]){
			v=top[v];
			if(f[v]==u) break;
			v=f[v];
		}
		if(f[v]!=u) v=son[u];
		if(dfn[v]>=2){
			update(1,1,n,1,dfn[v]-1,dfn[vp]);
			oedge[1]++,oedge[dfn[v]]--;
		}
		if(dfn[v]+sz[v]<=n){
			update(1,1,n,dfn[v]+sz[v],n,dfn[vp]);
			oedge[dfn[v]+sz[v]]++;
		}
	}
	for(int i=1;i<=n_tar;i++){
		if(!dfn_tar[i]) tarjan(i);
	}
	if(scc!=n_tar){
		for(int i=1;i<=n;i++) printf("0\n");
		return 0;
	} 
	for(int i=2;i<=n;i++){
		oedge[i]+=oedge[i-1];
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",oedge[dfn[i]]==0);
	}
	return 0;
}
posted @ 2023-11-06 13:12  ArizonaYYDS  阅读(17)  评论(0)    收藏  举报