习题:Catowice City(tarjan)

题目

传送门

思路

考虑用一种类似于2-sat的方法来推

如果i号人选,那么与他相识的猫就不能选,与之相对的,与他相识的猫的主人就必须选,考虑对其连单向边

比较明显的,在同一个强联通分量里面的点的状态是一样的,所以我们只需要看整个图是不是只有一个强联通分量就可以判断可行性,方案就只需要找到没有出度的强连通分量,全选人,其他全部选猫就可以

代码

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int t;
int n,m;
int dfn[1000005],low[1000005],bel[1000005],cnt,scc;
bool ins[1000005];
stack<int> s;
vector<int> g[1000005];
void tarjan(int u)
{
	s.push(u);
	ins[u]=1;
	dfn[u]=low[u]=++cnt;
	for(int i=0;i<g[u].size();i++)
	{
		int v=g[u][i];
		if(dfn[v]==0)
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		int v;
		scc++;
		do
		{
			v=s.top();
			s.pop();
			ins[v]=0;
			bel[v]=scc;
		}while(v!=u);
		
	}
}
void c_in()
{
	cin>>n>>m;
	cnt=scc=0;
	while(!s.empty())
		s.pop();
	for(int i=1;i<=n;i++)
	{
		dfn[i]=low[i]=ins[i]=bel[i]=0;
		g[i].clear();
	}
	for(int i=1,u,v;i<=m;i++)
	{
		cin>>u>>v;
		if(u==v)
			continue;
		g[u].push_back(v);
	}
	for(int i=1;i<=n;i++)
		if(dfn[i]==0)
			tarjan(i);
	if(scc==1)
		cout<<"No\n";
	else
	{
		int s[2]={};
		cout<<"Yes\n";
		for(int i=1;i<=n;i++)
			s[bel[i]!=1]++;
		cout<<s[0]<<' '<<s[1]<<'\n';
		for(int i=1;i<=n;i++)
			if(bel[i]==1)
				cout<<i<<' ';
		cout<<'\n';
		for(int i=1;i<=n;i++)
			if(bel[i]!=1)
				cout<<i<<' ';
		cout<<'\n';
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>t;
	for(int i=1;i<=t;i++)
		c_in();
	return 0;
}
posted @ 2020-08-24 10:30  loney_s  阅读(135)  评论(0)    收藏  举报