小Z爱图论(NOIP信(sang)心(bin)赛)From FallDream

题目:

小Z最近喜欢上了图论,于是他研究了一下图的连通性问题。但是他遇到了一个难题。

        给定一个n个点的有向图,求有多少点对(i,j)满足从i点出发能到达点j ?

        小Z仅会简单的朴素算法,所以他想问问你怎么解决。

        你能帮帮他吗?

输入/输出格式

 第一行一个数字n,表示有n个点。

 接下来n行,每行n个0/1,如果第i行第j个是1,表示从第i个点向第j个点有连边。

        输出仅包含一个数,表示满足条件的点对数量。

样例输入/输出

Input:

3

0 1 0

0 0 1

1 0 0

Output:

9

样例解释

       从任意一个点出发均可到达其它所有点(包括自己)

数据范围与约定

      对于50%的数据,满足n<=500

       对于100%的数据,满足n<=2000

       注意i和j可以相同

好吧,大家一看到这道题,心想:有向图判断连通,SB都会(蒟蒻瑟瑟发抖)。。

然后我们看到了数据范围。。

恩。。

好像O(n^3)不能过?

然后N(n^2logn)就能过了?

然后各种奇特算法就冒了出来。。

计算强联通分量。。。(tarjan+拓补?(by hzwer))

结果蒟蒻认为非常的难,就写了个暴力,拿了40分、

结果学长给出正解。

floyd....

floyd?不会T?

是的,不会T,因为我们拥有了一个新数据结构!

BITSET!!!!

科普一下。

首先用bitset要#include<bitset>

同时要定义的时候要这么写:bitset<位数> 名称([数组]可加可不加)

先来理解一下bitset到底是什么东东。

众所周知,1个bool值有2种可能 0或1

那么一个bitset就像是n个bool值用二进制叠了起来,如:001010

所以还是O(N)啊?

不对,因为一个二进制数为1bit

而一个int为32bit

所以复杂度为O(N^3)/32

神奇的过了。。

然后再来讲一讲具体的应用

首先bitset最常用的就是count()函数,他能找出bitset中1的数量(复杂度玄学)

本题中我们将floyd简化

原来3个循环,现在2个。

bitset<2005> b[2005]

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

if(b[j][i])b[j]|=b[i];

就是说如果j能走到i,那么i能走到的地方j一定能走到。

最后统计答案即可(b[i].count()的函数返回值即为i能到达的点的个数(包括i自己))

下面贴代码

#include<iostream>
#include<cstdio>
#include<bitset>
#define r register 
using namespace std;
bitset<2005> b[2005];
int n;
int ans;
int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d",&n);
    for(r int i=1;i<=n;i++)
        for(r int j=1;j<=n;j++)
        {
            int x;
            scanf("%d",&x);
            b[i][j]=(x|(i==j));
        }
    for(r int i=1;i<=n;i++)
    for(r int j=1;j<=n;j++)
    if(b[j][i])b[j]|=b[i];
    for(r int i=1;i<=n;i++)
    ans+=b[i].count();
    printf("%d
",ans);
    fclose(stdin);
    fclose(stdout);
}
原文地址:https://www.cnblogs.com/ghostfly233/p/6889846.html