思路求hack
查看原帖
思路求hack
358971
朦胧_XY楼主2022/11/14 19:41

思路:把每个强连通分量缩成点,缩出来的那个点的权值为整个强连通分量的点数,再求出新图里每个点能被多少个其它点(该点所代表的强连通分量点数)到达,最后看每个点所在缩点能否被图中所有点到达。

64pts

看测试点是错误在答案少了。

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int N = 10005, M = 50005;
int n, m, Sd[N], In[N], sum[N];
int cnt, dfn[N], Low[N], vis[N];
int tot, head[N], to[M << 1], nxt[M << 1], frm[M << 1];
vector<int> edg[N];
stack<int> stk;
queue<int> que;
void add(int x, int y){
	to[++tot] = y;
	frm[tot] = x;
	nxt[tot] = head[x];
	head[x] = tot;
}
void Tarjan(int x){
	Low[x] = dfn[x] = ++cnt;
	stk.push(x), vis[x] = 1;
	for(int i = head[x]; i; i = nxt[i]){
		if(!dfn[to[i]])
			Tarjan(to[i]), Low[x] = min(Low[x], Low[to[i]]);
		else if(vis[to[i]])
			Low[x] = min(Low[x], dfn[to[i]]);
	}
	if(Low[x] == dfn[x]){
		while(stk.top() != x){
			Sd[stk.top()] = x, sum[x] += sum[stk.top()];//缩出来的那个点的权值求和为整个强连通分量的点数
			vis[stk.top()] = 0, stk.pop();
		}
		Sd[x] = x, vis[x] = 0, stk.pop();
	}
}
int Topo(){
	int x, res = 0;
	for(int i = 1; i <= n; i++)
		if(Sd[i] == i && !In[i]) que.push(i);//把入度为0的点入队
	while(!que.empty()){
		x = que.front(), que.pop();
		for(int i : edg[x]){
			sum[i] += sum[x], In[i]--;//求出该强连通分量能被多少个点到达
			if(!In[i]) que.push(i);
		}
	}
	for(int i = 1; i <= n; i++)
		if(sum[Sd[i]] == n) res++;//答案为能被所有点到达的点数
	return res;
}
int main(){
	int x, y;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
		sum[i]++;//一开始每个点的点数为1
	for(int i = 1; i <= m; i++){
		scanf("%d%d", &x, &y);
		add(x, y);
	}
	for(int i = 1; i <= n; i++)
		if(!dfn[i]) Tarjan(i);
	for(int i = 1; i <= m; i++){
		x = Sd[frm[i]], y = Sd[to[i]];
		if(x != y) In[y]++, edg[x].push_back(y);
	}
	printf("%d\n", Topo());
	return 0;
}

2022/11/14 19:41
加载中...