LuoguP2341[HAOI2006]受欢迎的牛(强连通分量,缩点)

题目如下:
https://www.luogu.org/problemnew/show/P2341#sub
很明显,由于牛的爱慕会传递,a喜欢b,那么从a到b连一条有向边,跑一遍强连通分量,在一个强连通分量的人都互相爱慕,判断一个点能否被所有人喜爱故判断它是否能被所有强连通分量到达,也就是从该节点反向dfs若能到达所有强连通分量即可
代码如下,kosaraju缩点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
struct Node{
	int stime,etime,p;
}N[10010];
vector<int> E[10010],UE[10010],NE[10010],UNE[10010];
int n,m,vis[10010],step,vis2[10010],color[10010],count_color,sum,vis3[10010],colorsum[10010];
void dfs(int t){ //正图跑时间戳
	if(!vis[t]){
		N[t].stime=++step;
		N[t].p=t;
		vis[t]=1;
	}
	else return;
	for(int i=0;i<E[t].size();i++) dfs(E[t][i]);
	N[t].etime=++step;
}
void udfs(int t){//返图缩点
	if(!vis2[t]){
		vis2[t]=1;
		color[t]=count_color;
		colorsum[count_color]++;
	}
	else return;
	for(int i=0;i<UE[t].size();i++){
		udfs(UE[t][i]);
	}
}
void change_map(){
	for(int i=1;i<=n;i++) for(int j=0;j<E[i].size();j++) UE[E[i][j]].push_back(i); 
}
void change_mapN(){
	for(int i=1;i<=count_color;i++) for(int j=0;j<NE[i].size();j++) UNE[NE[i][j]].push_back(i);
}
void getmap(){
	for(int i=1;i<=n;i++) for(int j=0;j<E[i].size();j++) if(color[i]!=color[E[i][j]]) NE[color[i]].push_back(color[E[i][j]]);
}
bool cmp(Node a,Node b){
	return a.etime>b.etime;
}
void check(int t){
	if(!vis3[t]){
		vis3[t]=1;
		sum++;
	}
	else return;
	for(int i=0;i<UNE[t].size();i++) check(UNE[t][i]);
}
int main(){
 	scanf("%d%d",&n,&m);
 	for(int i=1;i<=m;i++){
 		int u,v;
 		scanf("%d%d",&u,&v);
 		E[u].push_back(v); 
	}
	for(int i=1;i<=n;i++) if(!vis[i]) dfs(i);
	change_map();
	sort(N+1,N+n+1,cmp);
	for(int i=1;i<=n;i++) if(!vis2[N[i].p]){count_color++;udfs(N[i].p);}
	getmap();
	change_mapN();
	int ans=0;
	for(int i=1;i<=count_color;i++){
		if(!NE[i].size()&&!vis3[i]){
			sum=0;
			check(i);
			if(sum==count_color) ans+=colorsum[i];
		}
	}
	cout<<ans<<endl;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值