BZOJ 2208 JSOI2010 连通数 Tarjan+拓扑排序

题目大意:给定一个n个点的有向图,求有多少点对(x,y),使x沿边可到达y

设f[i][j]为从i到j是否可达

首先强联通分量中的任意两个点均可达 于是我们利用Tarjan缩点

缩点之后是一个拓扑图,我们求出拓扑序,沿着拓扑序从后向前DP,状态转移方程为:

f[i][k]=or{ f[j][k] } (i有直连边到达j,1<=k<=n,n为强连通分量的个数)

鉴于每个点的值只会是1或者0,所以我们可以直接状压,或者干脆开bitset,整体取或即可

时间复杂度O(mn/32)

今天各种手滑。。。Tarjan不赋值dpt和low,拓扑序求出来不用,各种调用错数组。。。终于彻底脑残了好开心233 QAQ

#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 2014
using namespace std;
int n,ans,map[M][M],topo_map[M][M];
int dpt[M],low[M],v[M],cnt,belong[M],siz[M],_n,stack[M],top;
int into[M],q[M],r,h;
bitset<M>f[M];
void Tarjan(int x)
{
	int y;
	dpt[x]=low[x]=++cnt;
	stack[++top]=x;
	for(y=1;y<=n;y++)
		if(map[x][y])
		{
			if(v[y])
				continue;
			if(dpt[y])
				low[x]=min(low[x],dpt[y]);
			else
				Tarjan(y),low[x]=min(low[x],low[y]);
		}
	if(dpt[x]==low[x])
	{
		int t;
		++_n;
		do{
			t=stack[top--];
			belong[t]=_n;
			v[t]=1;
			++siz[_n];
		}while(t!=x);
	}
}
void Topology_Sort()
{
	int i,y;
	for(i=1;i<=_n;i++)
		if(!into[i])
			q[++r]=i;
	while(r!=h)
	{
		int x=q[++h];
		for(y=1;y<=_n;y++)
			if(topo_map[x][y])
			{
				into[y]--;
				if(!into[y])
					q[++r]=y;
			}
	}
}
int main()
{
	int i,j,x;
	cin>>n;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			scanf("%1d",&map[i][j]);
	for(i=1;i<=n;i++)
		if(!v[i])
			Tarjan(i);
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			if(map[i][j]&&belong[i]!=belong[j])
			{
				if(!topo_map[belong[i]][belong[j]])
					into[belong[j]]++;
				topo_map[belong[i]][belong[j]]=1;
				f[belong[i]][belong[j]]=1;
			}
	for(i=1;i<=_n;i++)
		f[i][i]=1;
	Topology_Sort();
	for(i=_n;i;i--)
	{
		x=q[i];
		for(j=1;j<=_n;j++)
			if(topo_map[x][j])
				f[x]|=f[j];
	}
	for(i=1;i<=_n;i++)
		for(j=1;j<=_n;j++)
			if(f[i][j])
				ans+=siz[i]*siz[j];
	cout<<ans<<endl;
}
时间: 2024-09-29 23:59:50

BZOJ 2208 JSOI2010 连通数 Tarjan+拓扑排序的相关文章

BZOJ 2208: [Jsoi2010]连通数( DFS )

n只有2000,直接DFS就可以过了... -------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cctype> #define rep( i, n ) for( int i = 0; i

bzoj 2208: [Jsoi2010]连通数

一看就是缩点拓扑啊,可数据范围这么小,暴力即可啊. 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<queue> 7 #include<algorithm> 8 #include<vector> 9 #define M 1000009 10 #

2208: [Jsoi2010]连通数 - BZOJ

Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图的连通数. Sample Input 3 010 001 100 Sample Output 9HINT 对于100%的数据,N不超过2000. 看到这题然后马上打了一个tarjan 然后对每一个强连通分量dfs,A了之后感觉有点奇怪,这个复杂度是多少来着,我好像算不出来,果断百度题解 然后大囧..

【BZOJ】2208 [Jsoi2010]连通数

[算法]强连通分量(tarjan)+拓扑排序+状态压缩(bitset) [题解] 1.强连通分量(scc)内所有点可互达,对答案的贡献为cnt[i]*cnt[i](cnt[i]第i个scc内点的个数),在第四步顺便计算即可,不用单独计算. 2.缩点得到新图,对新图中的每一个点开一个bitset[2000]来记录第i个点能否到达它,初始值为f[i][i]=1. bitset用法:http://blog.163.com/lixiangqiu_9202/blog/static/535750372012

2208: [Jsoi2010]连通数

2208: [Jsoi2010]连通数 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1371  Solved: 557[Submit][Status] Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图的连通数. Sample Input 3 010 001 100 Sample Output 9

BZOJ 3887 Usaco2015 Jan Grass Cownoisseur Tarjan+拓扑排序

题目大意:给定一张图,从1开始随便走最后回到1,有一次机会可以反向沿着某条边走一次,求最多能经过多少个点 显然如果没有反向的机会的话答案就是1号节点所在强连通分量的大小 现在有了这个机会 那么将某条边反向后 缩点之后的图形成了一个包含1号节点所在强连通分量的环 这样才能使答案增加 将这个环从反向的边和1号节点所在强连通分量处断开 发现这个环被拆成了两条链 一条从1出发,一条指向1 因此缩点后利用拓扑排序分别求出正图和反图中1号节点所在强连通分量到每个强连通分量的最长链 然后枚举每条边反转更新答案

【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图

思维难度不大,关键考代码实现能力.一些细节还是很妙的. Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图.若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图.若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图.给

bzoj 4010: [HNOI2015]菜肴制作 拓扑排序

4010: [HNOI2015]菜肴制作 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/67 Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号为1.由于菜肴之间口味搭配的问题, 某些菜肴必须在另一些菜肴之前制作,具体的,一共有 M 条形如“i 号菜肴‘必须’

BZOJ 4010 HNOI2015 菜肴制作 拓扑排序+堆

题目大意:给定一张无向图,求一个拓扑序,使: 1的位置最靠前 在保证上面的条件下使2的位置最靠前 在保证上面的条件下使3的位置最靠前 -- 注意不是字典序最小!例如样例3 建立反图,对反图求字典序最大的拓扑序,然后反向输出即可. 我不知道为什么.真的不知道. 求个解答在线等. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 1001