P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G

模板记录

P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G_第1张图片

#include
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define x first
#define y second
#define int long long
using namespace std;
typedef long long ll;
typedef pair pii;
const int mod = 1e9 + 7;
const int N = 2e5+ 10;
int dfn[N], low[N], timestamp;
int stk[N], id[N], all[N];
bool in_stk[N];
int h[N], e[N], ne[N], idx;
int n, m, top, scc_cnt;
inline void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}



void tarjan(int u)
{
	// 记录序号以及初始化可以到达的最小标号点
	dfn[u] = low[u] = ++ timestamp;
	// 把当前节点放入栈内, 并且标记该节点在栈内
	stk[++ top] = u, in_stk[u] = true;

	// 便利当前节点的所有节点
	for(int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if(!dfn[j]) // 该点未遍历
		{
			tarjan(j); // 便利子节点
			low[u] = min(low[u], low[j]); // 更新当前节点可以到达的最小序号点
		}
		else if(in_stk[j])// 该点遍历过,但是在栈内,表明形成了一个回路
			low[u] = min(low[u], low[j]);
	}
	if(dfn[u] == low[u])
	{
		int y;
		++ scc_cnt;
		do
		{
			y = stk[top --];
			in_stk[y] = false;
			id[y] = scc_cnt;
			all[scc_cnt] ++;
		}
		while(y != u);
	}
}
int du[N];

inline void sovle()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for(int i = 0; i < m; i ++)
	{
		int a, b;
		cin >> a >> b;
		add(a, b);
	}
	for(int i = 1; i <= n; i ++)
		if(!dfn[i])
			tarjan(i);

	for(int i = 1; i <= n; i ++)
	{
		for(int j = h[i]; ~j; j = ne[j])
		{
			int k = e[j];
			if(id[i] != id[k])
				du[id[i]] ++;
		}
	}

	int s = 0;
	for(int i = 1; i <= scc_cnt; i ++ )
		if(du[i] == 0)
		{
			if(s != 0)
			{
				cout << "0" << endl;
				return ;
			}
			s = i;
		}
	cout << all[s] << endl;

}
signed main(void)
{
	IOS;
	int t = 1;
//  cin >> t;
	while(t --) sovle();
	return 0;
}

把整个图变成一个强连通块儿所需要添加的最小边数量,ans1(入度为0块儿的数量),ans2(出度为0块儿的数量),最小边数为max(ans1, ans2);

inline void sovle()
{
	cin >> n;
	memset(h, -1, sizeof h);
	for(int i = 1; i <= n; i ++){
		int a;while(cin >> a, a)add(i, a);
	}
	for(int i = 1; i <= n; i ++)
		if(!dfn[i])
			tarjan(i);
	for(int i = 1; i <= n; i ++){
		for(int j = h[i]; ~j; j = ne[j]){
			int k = e[j];
			if(id[i] != id[k]) in[id[k]] ++, out[id[i]] ++;
		}
	}
	int ans1 = 0, ans2 = 0;
	for(int i = 1; i <= scc_cnt; i ++){
		if(!in[i]) ans1 ++; // 入度为0块儿的数量
		if(!out[i]) ans2 ++; // 出度为0块儿的数量
	}
	cout << ans1 << endl;
	if(scc_cnt == 1) ans1 = ans2 = 0; // 若只有一个块儿,则不需要添加边
	cout << max(ans2, ans1) << endl; // 取最大值即可
}

你可能感兴趣的:(图论)