点对(强连通)

第2题     点对 查看测评数据信息

给定一个N个节点的有向图,统计该有向图的点对个数,如下图:

image.png

顶点1可达1,2,3,4,5

顶点2可达2,3,4,5

顶点3可达3,4,5

顶点4,5都只能到达自身。

所以这张图的点对数为14.

给定一张图,请你求出它的点对数

对于100%的数据,1≤N≤2000

输入格式

 

输入数据第一行是图顶点的数量,一个正整数N.

接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

 

输出格式

 

输出一个整数,表示点对数

 

输入/输出例子1

输入:

3

010

001

100

 

输出:

9

 

样例解释

 

 

想知道图中哪个点能到哪个点,1.建反图(拓扑),2.用bitset或运算,具体看本题

缩点,一个胖点内的点可以互相到达,所以另外一个胖点到这个胖点的点对数量就是相乘他们所包含的成员数即可

缩点后建个新图,我们可以统计每个胖点内点的个数,再统计从哪个点能到达哪个点,如果可以记录答案

假设u能到v,那么 ans+=size[u]*size[v],自身也要加到答案,因为有size[u]个点,胖点内每个点到每个点,也就是size[u]*size[u]

现在问题变成,怎么求u能到v,也就是统计哪个点能到哪个点

 

由于1能到的点是2,同时,2能到的点1也能到,所以1能到的点,就是他能到的点,所能到的点,一直推下去

定f[u][v]=1,表示u能到v

那么如果f[1][2]=1,那么f[1][3]=1,f[1][4]=1

 

策略:建反图,这样就能从图中最底下的点,反推到最上面的点,例如4能到3,3能到2,这时再跑到1,就可以知道1号点他能到的点,所能到的点是什么了

技巧:用bitset位运算的“|”,用二进制01代表能否到达某个点,例如此图的3号点,f[3]=0011,(注意,点到自身也是可以的)

我们只需要O(1)的时间,就能计算出 f[2] |= f[3] =0111

 

#include <bits/stdc++.h>
using namespace std;
const int N=2005;

int n, vis[N], ans=0, dfn[N], low[N], idx=0, cnt=0, id[N], size[N], d[N];
stack<int> st;
char c[N];
vector<int> a[N], b[N];
bitset<N> f[N];
queue<int> q;
void dfs(int u)
{
	dfn[u]=low[u]=++idx;
	st.push(u);
	
	for (int i=0; i<a[u].size(); i++)
	{
		int v=a[u][i];
		if (!dfn[v])
		{
			dfs(v);
			low[u]=min(low[u], low[v]);
		}
		else if (!id[v]) low[u]=min(low[u], dfn[v]);
	} 
	
	if (low[u]==dfn[u])
	{
		cnt++;
		while (st.top()!=u)
		{
			id[st.top()]=cnt;
			size[cnt]++;
			st.pop();
		}
		id[st.top()]=cnt;
		size[cnt]++;
		st.pop();
	}
}
void topsort()
{
	for (int i=1; i<=cnt; i++)
		if (!d[i]) q.push(i);
		
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		
		for (int i=0; i<b[u].size(); i++)
		{
			int v=b[u][i];
			f[v]|=f[u];
			d[v]--;
			if (!d[v]) q.push(v);
		}
	}
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++)
	{
		scanf("%s", c+1);
		for (int j=1; j<=n; j++)
			if (c[j]=='1') a[i].push_back(j);
	}
	
	for (int i=1; i<=n; i++)
		if (!dfn[i]) dfs(i);
	
	for (int u=1; u<=n; u++)
		for (int j=0; j<a[u].size(); j++)
		{
			int v=a[u][j];
			if (id[u]!=id[v]) b[id[v]].push_back(id[u]), d[id[u]]++;
		}
	
	for (int i=1; i<=cnt; i++) f[i][i]=1;
	topsort();
	
	
	for (int i=1; i<=cnt; i++)
		for (int j=1; j<=cnt; j++)
			if (f[i][j]) ans+=size[i]*size[j];
	
	printf("%d", ans);
	
	return 0;
}
/*
5
01100
00101
00011
00000
00000
*/

  

posted @ 2024-07-18 19:16  cn是大帅哥886  阅读(60)  评论(0)    收藏  举报