bzoj 2208: [Jsoi2010]连通数 (dfs|tarjan+bitset+拓扑序)

本文介绍了一道JSOI2010竞赛题目“连通数”的解题思路,采用DFS和Tarjan算法求解图的连通数,并通过拓扑排序优化算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2208: [Jsoi2010]连通数

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 2365   Solved: 1001
[ Submit][ Status][ Discuss]

Description

Input

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

Output

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

Sample Input

3
010
001
100

Sample Output

9

HINT

对于100%的数据,N不超过2000。

Source

[ Submit][ Status][ Discuss]

题解:dfs

数据范围很小,直接暴力即可。

貌似正解是tarjan+拓扑序

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 5000003
using namespace std;
int n,m;
int point[N],next[N],v[N],ans,vis[N],num,tot;
char s[3003];
void add(int x,int  y)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int now,int x)
{
	vis[x]=now; ans++;
	for (int i=point[x];i;i=next[i])
	 if (vis[v[i]]<now) dfs(now,v[i]);
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) {
		scanf("%s",s+1);
		for (int j=1;j<=n;j++) 
		 if (s[j]=='1') add(i,j);
	}
	for (int i=1;i<=n;i++) 
		dfs(i,i);
	printf("%d\n",ans);
}

tarjan+bitset+拓扑序

tarjan缩点后,每次块内的点可以相互到达,ans+=num[i]*num[i],num[i]表示的是强连通块内点的个数。

然后考虑块与块之间的影响,按照拓扑序进行更新,然后用bitset记录块与块之间的到达关系,记录答案即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<bitset>
#define N 5000003
#define M 2003
using namespace std;
int n,m,top;
int point[M],next[N],v[N],vis[M],num[N],tot,cnt1,sum[N],ans,sz,map[M][M];
int x[N],y[N],cnt,dfsn[M],st[N],belong[M],ins[M],low[M],outs[M];
char s[3003];
bitset<M> mark[M];
void add(int x,int  y)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
	//cout<<x<<" "<<y<<endl;
}
void tarjan(int x)
{
	low[x]=dfsn[x]=++sz;
	ins[x]=1; st[++top]=x;
	for (int i=point[x];i;i=next[i])
	 {
	 	int j=v[i];
	 	if (!dfsn[j]) tarjan(j),low[x]=min(low[x],low[j]);
	 	else if(ins[j]) low[x]=min(low[x],dfsn[j]);
	 }
	if(low[x]==dfsn[x]) {
		++cnt;
		int j;
		do{
			j=st[top--]; num[cnt]++;
			belong[j]=cnt; ins[j]=0;
		}while(j!=x);
		sum[cnt]=num[cnt];
		ans+=num[cnt]*num[cnt];
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) {
		scanf("%s",s+1);
		for (int j=1;j<=n;j++) 
		 if (s[j]=='1') add(i,j),x[++cnt1]=i,y[cnt1]=j;
	}
	for (int i=1;i<=n;i++)
	 if (!dfsn[i]) tarjan(i);
	tot=0;
	memset(point,0,sizeof(point));
	memset(next,0,sizeof(next));
	memset(ins,0,sizeof(ins));
	//cout<<endl;
	for (int i=1;i<=cnt1;i++)
	 if (belong[x[i]]!=belong[y[i]]) {
	   int t=belong[x[i]]; int t1=belong[y[i]];
	   map[t][t1]=1;
	 }
	for (int i=1;i<=cnt;i++)
	 for (int j=1;j<=cnt;j++)
	  if (map[i][j]) add(i,j),ins[j]++,outs[i]++;
	queue<int> p; 
	for (int i=1;i<=cnt;i++) if (!ins[i]) p.push(i);
	for (int i=1;i<=cnt;i++) mark[i][i]=1;
	while (!p.empty()) {
		int now=p.front(); p.pop();
		int t=0;
		for (int i=1;i<=cnt;i++) if(mark[now][i]&&i!=now) t+=num[i];
		ans+=t*num[now];
		for (int i=point[now];i;i=next[i])
		 {
		 	mark[v[i]]|=mark[now];
		 	ins[v[i]]--;
		 	if (!ins[v[i]]) p.push(v[i]);
		 }
	}
	printf("%d\n",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值