2208: [Jsoi2010]连通数(Trajan+bitset)

2208: [Jsoi2010]连通数

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 2929   Solved: 1280
[ Submit][ Status][ Discuss]

Description

Input

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

Output

输出一行一个整数,表示该图的连通数。

Sample Input

3
010
001
100

Sample Output

9


求一波强连通分量,预处理所有的scc[],其中所有在同一强连通分量里的点scc值相同

然后缩点形成一张有向无环图(可拓扑图)

很显然每个scc自身对答案的贡献就是siz(scc)²

不同scc之间的贡献在拓扑图上总体进行dp就好了

注意这里不用再求拓扑序,因为Trajan之后scc本身就已经有序了!

假设x点的scc值高,y点的scc值低,那么x点和y点要不不连通,要不只能从x到y!


bieset会方便的多

out[a][b]表示第a个scc与第b节点的联通关系

#include<stdio.h>
#include<vector>
#include<bitset>
#include<stack>
#include<algorithm>
using namespace std;
vector<int> G[2005];
stack<int> st;
bitset<2005> out[2005], temp;
int t, cnt, scc[2005], low[2005], Time[2005], sum[2005];
void Trajan(int u)
{
	int i, v;
	st.push(u);
	low[u] = Time[u] = ++t;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(Time[v]==0)
		{
			Trajan(v);
			low[u] = min(low[v], low[u]);
		}
		else if(scc[v]==0)
			low[u] = min(low[u], Time[v]);
	}
	if(low[u]==Time[u])
	{
		++cnt;
		while(st.empty()==0)
		{
			v = st.top();
			st.pop();
			scc[v] = cnt;
			sum[cnt]++;
			if(v==u)
				break;
		}
	}
}
int main(void)
{
	int n, i, j, x, k, ans, v;
	scanf("%d", &n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			scanf("%1d", &x);
			if(x==1)
				G[i].push_back(j);
		}
	}
	for(i=1;i<=n;i++)
	{
		if(Time[i]==0)
			Trajan(i);
	}
	for(i=1;i<=n;i++)
		out[scc[i]][i] = 1;
	ans = 0;
	for(i=1;i<=cnt;i++)
	{
		temp.reset();
		for(j=1;j<=n;j++)
		{
			if(scc[j]!=i)
				continue;
			for(k=0;k<G[j].size();k++)
			{
				v = G[j][k];
				if(scc[v]<i)
					temp |= out[scc[v]];
			}
		}
		out[i] |= temp;
		ans += sum[i]*out[i].count();
	}
	printf("%d\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值