把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5157】[USACO18DEC] The Cow Gathering P(拓扑)

点此看题面

  • 给定一棵\(n\)个点的树,每次可以选择删除一个叶节点。
  • \(m\)个限制\((x,y)\),表示\(x\)必须先于\(y\)被删除。
  • 要求判断每个点是否可能成为最后剩下的点。
  • \(n,m\le10^5\)

拓扑求一个可行解

其实就一个很简单的贪心想法,我们每次选择一个不受限制的叶节点删去。

也就是说,要求剩余度数\(\le1\)且剩余受限制数\(=0\),直接拓扑排序即可。

如果找不到这样一个可行解,就说明无解了。

从一解到全解

以求出来的这一个可行解为根。

考虑一个限制\((x,y)\),相当于以\(y\)为根时的\(x\)子树内所有点都不可能成为最后剩下的点。

由于当前以一个可行解为根,\(y\)一定不在\(x\)子树内,故以\(y\)为根时的\(x\)子树就是当前的\(x\)子树。

直接给\(x\)打个标记,然后从\(rt\)出发\(dfs\)一遍,不访问任何有标记的点,则被访问到的点就是答案。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,deg[N+5],cnt[N+5],p[N+5],ans[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[2*N+5];vector<int> G[N+5];
int q[N+5];I int Topo()//拓扑求一个可行解
{
	RI i,k,H=1,T=0;vector<int>::iterator it;for(i=1;i<=n;++i) deg[i]==1&&!cnt[i]&&(q[++T]=i);W(H<=T)
	{
		for(i=lnk[k=q[H++]];i;i=e[i].nxt) --deg[e[i].to]==1&&!cnt[e[i].to]&&(q[++T]=e[i].to);//更新度数
		for(it=G[k].begin();it!=G[k].end();++it) !--cnt[*it]&&deg[*it]<=1&&(q[++T]=*it);//更新受限制数
	}return T^n?-1:q[T];//无解返回-1,否则返回拓扑排序中最后一个点
}
I void dfs(CI x,CI lst=0) {ans[x]=1;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&!p[e[i].to]&&(dfs(e[i].to,x),0);}//遍历,不访问任何有标记的点
int main()
{
	RI i,x,y;for(scanf("%d%d",&n,&m),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x),++deg[x],++deg[y];
	for(i=1;i<=m;++i) scanf("%d%d",&x,&y),++cnt[y],G[x].push_back(y);
	RI rt=Topo();if(~rt) {for(i=1;i<=n;++i) !G[i].empty()&&(p[i]=1);dfs(rt);}//给有限制的点打上标记
	for(i=1;i<=n;++i) putchar(48|ans[i]),putchar('\n');return 0;
}
posted @ 2021-05-31 09:59  TheLostWeak  阅读(39)  评论(0编辑  收藏  举报