初见安~这里是传送门:洛谷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
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——