[HAOI2006]受欢迎的牛

原题传送门

题目大意

现在有 N N N 头牛,给你 M M M 对整数 ( A , B ) (A,B) (A,B),表示牛 A A A 认为牛 B B B 受欢迎。 这种关系是具有传递性的,如果 A A A 认为 B B B 受欢迎, B B B 认为 C C C 受欢迎,那么牛 A A A 也认为牛 C C C 受欢迎。你的任务是求出有多少头牛被所有的牛认为是受欢迎的。

注意:

给出的信息有可能重复,即有可能出现多个 A A A B B B

数据范围:

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N\le10^4 1N104 1 ≤ M ≤ 5 × 1 0 4 1\le M\le5\times 10^4 1M5×104

解题思路

一眼看到数据范围就可以想到用 Tarjan 进行 缩点

关于 Tarjan,可以看本蒟蒻的博客

关于 缩点,也可以看本蒟蒻的博客

如果一个强连通分量内的一点 x x x,可以到除这个强连通分量的点 y y y,就说明点 x x x 所在的强连通分量的所有点都可以到达点 y y y,所以就需要 缩点

这时,可以发现答案只能是一个强连通分量,而不是两个或多个。

此时,可以有一个定理:

缩点后的图是一个 DAG (有向无环图)。

关于本题,又可以有一个定理:

若缩点后的图出度为 0 0 0 的点只有一个,这个点就是答案。

证明:如果出度为 0 0 0 的点有多个呢?
显然,如果出度为 0 0 0 的点有 x 个,设为 a1、a2、a3、a4 ... ax,那么假设有一个点为答案,但是 a a a 集合内的所有点都不与它连通,所以这个点就不是答案,那么可以得出, a a a 集合内点使得这组输入没有答案。

证明:为什么缩点后的图出度为 0 0 0 的点只有一个,这个点就是答案。
假设点 x x x 是答案且他的出度不为 0 0 0。即所有点都可以到 x x x,但可以发现一个问题,点 x x x 和点 x x x 所连向的点应该在同一个强连通分量内,即形成了环,因为上面说了缩点后的图是一个 DAG (有向无环图),所以设据得假。

AC CODE

省略快读,orz

#include <bits/stdc++.h>
using namespace std;

inline int read()
{
    char c = getchar();
    int x = 0;
    bool f = 0;
    for (; !isdigit(c); c = getchar())
    {
        f ^= !(c ^ 45);
    }
    for (; isdigit(c); c = getchar())
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
    }
    if (f)
    {
        x = -x;
    }
    return x;
}

inline void write(int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
    {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

int T, n, m, ans, opt, cnt_node, cntn;
array<int, 200005> p;

int cnt;
array<int, 200005> head;
struct abc
{
	int to, nxt;
};
array<abc, 200005> dd;

int cnt_;
array<int, 200005> head_, out; //, ind;
array<abc, 200005> dd_;

array<bool, 200005> vis;
array<int, 200005> dfn, low, id;
stack<int> s;

array<int, 200005> dis;
queue<int> q;



inline void add(int u, int v)
{
	dd[++cnt].to = v;
	dd[cnt].nxt = head[u];
	head[u] = cnt;
}

inline void add_(int u, int v)
{
	dd_[++cnt_].to = v;
	dd_[cnt_].nxt = head_[u];
	head_[u] = cnt_;
}

inline void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt_node;
	s.push(u);
	vis[u] = 1;
	for (int e = head[u]; e; e = dd[e].nxt)
	{
		if (!dfn[dd[e].to])
		{
			tarjan(dd[e].to);
			low[u] = min(low[dd[e].to], low[u]);
		}
		else if (vis[dd[e].to])
			low[u] = min(low[u], dfn[dd[e].to]);
	}
	if (low[u] == dfn[u])
	{
		cntn++;
		while (1)
		{
			int now = s.top();
			s.pop();
			vis[now] = 0;
			id[now] = cntn;
			p[cntn]++;
			if (now == u) break;
		}
	}
}

void build()
{
	for(int i = 1; i <= n; ++i)
		for(int j = head[i]; j; j = dd[j].nxt)
		{
			int v = dd[j].to;
			if(id[i] != id[v])
			{
//				add_(id[i], id[v]);
//				cout<<id[i] <<" "<<id[v]<<"\n";
//				ind[id[v]]++;
				out[id[i]]++;
			}
		}
}

//void topu()
//{
//	for(int i = 1; i <= cntn; ++i)
//		if(!ind[i])
//		{
//			q.push(i);
//			dis[i] = p[i];
			cout<<i<<endl;
//		}
//	while(!q.empty())
//	{
//		int x = q.front();
//		q.pop();
//		for(int i = head_[x]; i; i = dd_[i].nxt)
//		{
//			int v = dd_[i].to;
//			dis[v] = dis[x] + p[v];
//			ind[v]--;
//			if(!ind[v]) q.push(v);
//		}
//	}
//}

signed main()
{
//	freopen("input.txt", "r", stdin);freopen("15.out", "w", stdout);
	n = read();
	m = read();
	for(int i = 1; i <= m; ++i)
	{
		int a, b;
		a = read();
		b = read();
		add(a, b);
	}
	for(int i = 1; i <= n; ++i)
		if(!dfn[i]) tarjan(i);
	build();
	for(int i = 1; i <= cntn; ++i)
		if(!out[i]) opt++, ans = p[i];
	if(opt == 1) write(ans);
	else write(0);
//	topu();
//	for(int i = 1; i <= cntn; ++i)
//		if(dis[i] == n) ans += p[i];
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值