1. 程式人生 > 實用技巧 >習題:Catowice City(tarjan)

習題: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;
}