洛谷·[HAOI2006]受欢迎的牛

初见安~这里是传送门:洛谷P2341

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当明星。

输入格式:

第一行:两个用空格分开的整数:N和M

第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

输出格式:

第一行:单独一个整数,表示明星奶牛的数量

输入样例:

3 3
1 2
2 1
2 3

输出样例:

1

说明

只有 3 号奶牛可以做明星

【数据范围】

10%的数据N<=20, M<=50

扫描二维码关注公众号,回复: 5770940 查看本文章

30%的数据N<=1000,M<=20000

70%的数据N<=5000,M<=50000

100%的数据N<=10000,M<=50000

题解

如果暴力运算的话,要从入度为0的点拓扑开始并且不断更新不断更新,能不能操作都不好说,主要是有环的情况很不好处理。所以我们就会用到tarjan缩点(tarjan传送门:Tarjan)——每一个强连通分量里的奶牛就打一个包,存一下这一群奶牛的数量,使这个图变为有向无环图,就可以处理啦!至于缩点后有没有必要再开一个邻接表来存,我们暂且不考虑。

那么作为有向无环图,接下来怎么操作呢——有两种做法(前一种就是我一开始的方法):

1.统计各个强连通分量入度,拓扑bfs存连接到的点,并更新各个强连通分量受爱慕的奶牛数,这就是要再开一个邻接表才能做到的。转移方程为:like[ i ]+=like[ y ] + 1; 代码量大概120行左右。

2.反向思维——统计各个强连通分量的出度。因为我们枚举几个例子就可以很容易发现:一个连通的有向图无环图,如果只有一个出度为0的点,那么其他每个点都可以到达这个点。奶牛这道题亦同——缩点后统计出度,最后扫一遍有多少出度为0的强连通分量,只有一个那么这一群奶牛都是受欢迎的,否则没有受欢迎的。这里似乎有一个漏洞——如果这个图本身就不连通呢?那出度为0的点就不可能只有一个,同样会达到同样的结果——没有受欢迎的奶牛。这种思路的话代码量不超过100行,也会简洁很多。

下面的代码及详解就是思路2做出来的——

#include<bits/stdc++.h>
#define maxn 10005
#define maxm 50005
using namespace std;
int n,m;
int a,b,outd[maxn],flag = 0,ans[maxn];
struct edge
{
	int to,nxt;
	edge(){}
	edge(int tt,int nn)
	{
		to=tt;nxt=nn;
	}
}e[maxm];

int k = 0,head[maxn];
void add(int u,int v)
{
	e[k] = edge(v, head[u]);
	head[u] = k++;
}

int dfn[maxn],low[maxn],tot=0,top=0,stc[maxn],cnt=0,col[maxn];
bool vis[maxn];
void tarjan(int x)//tarjan缩点模板
{
	dfn[x] = low[x] = ++tot;
	stc[++top] = x;
	vis[x] = 1;
	
	int y;
	for(int i = head[x]; ~i; i = e[i].nxt)
	{
		y = e[i].to;
		if(!dfn[y])
		{
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if(vis[y])
			low[x] = min(low[x], dfn[y]);
	}
	
	if(dfn[x] == low[x])
	{
		cnt++;
		do
		{
			y = stc[top--];
			vis[y] = 0;
			col[y] = cnt;
			ans[cnt]++;//ans存这个强连通分量里的奶牛数。
		}while(x != y);
	}
}

int judge()
{
	for(int i = 1; i <= cnt; i++)
	{
		if(!outd[i])
		{
			if(!flag) flag= i;
			else return 0;//flag已经更新过了,不止一个出度为0的了。
		}
	}
	return ans[flag];
}

int main()
{
	memset(head, -1, sizeof head);
	memset(head_,-1,sizeof head_);
	scanf("%d%d",&n, &m);
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d", &a, &b);
		add(a,b);
	}
	
	for(int i = 1; i <= n; i++)
		if(!dfn[i]) tarjan(i);
	
	int y; 
	for(int i = 1; i <= n; i++)
	{
		for(int j = head[i]; ~j; j = e[j].nxt)
		{
			y = e[j].to; 
			if(col[i] != col[y]) outd[col[i]]++;//存出度
		}
	}
	
	printf("%d", judge());
	return 0;
}

迎评:)
——End——

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/88724560