题解 P2835 【刻录光盘】

这个题别的大佬好像都用并查集,我觉得只需一遍 tarjan 就可以过掉,事实证明确实是这样 qwq 。


首先读入,存边。然后对这个有向图跑一遍 tarjan 求强连通分量。然后扫描所有边,如果两个端点不在同一个强连通分量里,那么就将入度增加 1 ,最后所有入度为0的强连通分量即为所求。

由强连通分量的基本知识我们可以知道,只要其中有一个点有了资料,那么其他的点都可以得到资料。在缩点之后,强连通分量之间又可以互相传递资料,那么就看入度为0的就行了。

关于 tarjan出门右转不谢。

#include<bits/stdc++.h>
using namespace std;
struct node {
	int fr,to,nxt;
} b[200*200+5];
int cnt,h[205];
void add(int x,int y) {
	b[++cnt].nxt=h[x];
	b[cnt].to=y;
	b[cnt].fr=x;
	h[x]=cnt;
}
int rd[205],id,dfn[205],vis[205],low[205],tarn,bl[205];//rd表示入度,bl表示belong于哪一个强连通分量。
stack<int> s;
void tarjan(int u) {//tarjan算法
	dfn[u]=low[u]=++id;
	s.push(u);
	vis[u]=1;
	for(int i=h[u]; i; i=b[i].nxt) {
		int v=b[i].to;
		if(!dfn[v]) {
			tarjan(v);
			low[u]=min(low[u],low[v]);
		} else if(vis[v]) {
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(dfn[u]==low[u]) {
		tarn++;
		int v;
		do {
			v=s.top();
			bl[v]=tarn;
			s.pop();
			vis[v]=0;
		} while (u!=v);
	}
}
int main() {
	int n;
	cin>>n;
	for(int i=1; i<=n; i++) {
		int x=1;
		while(x!=0) {
			cin>>x;
			if(x!=0) add(i,x);
		}
	}
	for(int i=1; i<=n; i++)
		if(!dfn[i]) tarjan(i);
	for(int i=1; i<=cnt; i++)//统计入度
		if(bl[b[i].fr]!=bl[b[i].to]) rd[bl[b[i].to]]++;//注意加bl
	int ans=0;
	for(int i=1; i<=tarn; i++)
		if(!rd[i]) ans++;
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/ahawzlc/p/12636849.html