P2341

[USACO03FALL / HAOI2006] 受欢迎的牛 G

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 \(A\) 喜欢 \(B\)\(B\) 喜欢 \(C\),那么 \(A\) 也喜欢 \(C\)。牛栏里共有 \(N\) 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

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

接下来 \(M\) 行:每行两个用空格分开的整数:\(A\)\(B\),表示 \(A\) 喜欢 \(B\)

输出格式

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

样例 #1

样例输入 #1

3 3
1 2
2 1
2 3

样例输出 #1

1

提示

只有 \(3\) 号奶牛可以做明星。

【数据范围】

对于 \(10\%\) 的数据,\(N\le20\)\(M\le50\)

对于 \(30\%\) 的数据,\(N\le10^3\)\(M\le2\times 10^4\)

对于 \(70\%\) 的数据,\(N\le5\times 10^3\)\(M\le5\times 10^4\)

对于 \(100\%\) 的数据,\(1\le N\le10^4\)\(1\le M\le5\times 10^4\)

对于一个环内的点可以缩点

tarjan完过后建新图

统计出度!!! out[i]

因为缩点过后新图为 DAG 所以最后的"点"(只有入度没有出度)即为答案

最后 若 out[i]==0 则 ans=num[i] (i所属强连通分量内点的个数)

若有 >1个 out[i]==0 则 没有明星 因为这些牛之间无法互相喜欢

不能统计入度 in[i]==tot-1 因为其它scc不一定要与ansのscc直接相连 可以间接传递!!! 所以统计出度才是正解!

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e4+5;
int n,m;
struct Graph{
	int nxt,to,from;
}edge[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
	cnt++;
	edge[cnt].to=v;
	edge[cnt].from=u;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],low[N],times,stack_[N],pt,vis[N],num[N],belong[N],tot;
void tarjan(int x)
{
	dfn[x]=low[x]=++times;
	stack_[++pt]=x;
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else{
			if(vis[v])
				low[x]=min(low[x],dfn[v]); 
		}
	}
	if(dfn[x]==low[x])
	{
		tot++;
		while(1)
		{
			belong[stack_[pt]]=tot;
			vis[stack_[pt]]=0;
			num[tot]+=1;
			pt--;	
			if(stack_[pt+1]==x)break;
		} 
	}
}
int out[N];
vector<int>cb[N];
vector<int>rdr[N];
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=cnt;i++)
	{
		if(belong[edge[i].from]!=belong[edge[i].to])
		{
			int x=belong[edge[i].from];
			int y=belong[edge[i].to];
			out[x]++;
			cb[x].push_back(y);
			rdr[y].push_back(x);
		}	
	}
	int pd=0,ans;
	for(int i=1;i<=tot;i++)
		if(!out[i])
			ans=num[i],pd++;
	if(pd>1)cout<<0<<"\n";
	else cout<<ans<<"\n";
	return 0;
}

确实感觉有了tarjan缩点算法后很多图上的环的问题都可以迎刃而解了

posted @ 2023-04-19 18:43  N0zoM1z0  阅读(7)  评论(0编辑  收藏  举报