简单路径-bitset初体验

题目描述

给定一个nn个点的无向图,求这个图中有多少条长度为4的简单路径。
n1500

输入

第一行一个数n
接下来n行每行n个0或1
第i行第j列是1表示i与j联通

输出

输出简单路径的个数

样例输入

5 00011 00000 00010 10100 10000

样例输出

2

提示

n<=1500

 
 
复制一下cgt大佬的ppt(未经授权hhh)

O(n^4)
不会做??
你可以离开这个教室了
暴力枚举四个城市即可
O(n^3)
70分做法:设经过的点为a-b-c-d,枚举中间边b-c,再枚举与b,c相连的点,设deg x 表示点x的度数,那么边b-c对答案的贡献为(du[b]-1)(du[c]- 1) - 经过b-c这条边的三元环个数。 计算三元环的个数只需要枚举除b,c之外的另一个点即可,时间复杂度O(n^3)
70分算法的瓶颈在于三元环计数
那如何解决呢

so easy
我们能想到压位
记s[i][1]为i和1~32的状态
第x位上是1的话就说明它和x号城市有连边
s[i][2]为i和33~64号……以此类推
i点和j点形成三元环的个数s可以这样写出
for k=1 ~ n/32  s=s+(s[i][k]&s[j][k] 中1的个数)
前面提到统计1的个数可以预处理出
所以时间复杂度变为了O(n^3 / 32)
n=1500时大约为1s
因此我们小小地使用了一个压位技巧便能AC此题
题目源JZOJ4857

所以我开始了手动bitset(程序灰常的丑嘤嘤嘤)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[1605][1605],s[1600][50],f[1605],b[66000];
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<(1<<16);i++) b[i]=b[i>>1]+(i&1);
        
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            scanf("%1d",&a[i][j]);
            if (a[i][j]==1) 
            {
                f[i]++;
            }
        }
     }
     
    for (int i=1;i<=n;i++)
        for (int j=1;j<=(n-1)/30+1;j++)
            for (int k=(j-1)*30+1;k<=j*30;k++)
                s[i][j]=(s[i][j]<<1)|a[i][k];
    ll num=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            if (a[i][j]==1)
                {
                    for (int k=1;k<=(n-1)/30+1;k++) 
                    {
                        int xx=s[i][k]&s[j][k];
                        int xxx=xx>>16;
                        xx=xx&((1<<16)-1);
                        num=num-b[xxx]-b[xx];
                    }
                     num=num+(f[i]-1)*(f[j]-1);
                }
        }
    printf("%lld",num);
}

然而我发现c++这种神奇的东西还有美丽的bitset,于是不用白不用哈

#include<bits/stdc++.h>
using namespace std;
bitset<1600>a[1600];
int num[1600];
int main()
{
    int n;
    long long sum;
    cout<<sum;
    scanf("%d",&n);
    for (int i=0;i<n;i++) cin>>a[i];
    for (int i=0;i<n;i++) num[i]=a[i].count();
    for (int i=0;i<n;i++) 
        for (int j=0;j<n;j++) 
            if ((i!=j)&&(a[i][n-j-1]==1)) 
            {
                sum=sum+(num[i]-1)*(num[j]-1)-(a[i]&a[j]).count();
            }
    printf("%lld",sum);
    return 0;
}

是不是很简单呢啦啦啦。

就是酱紫。

真是充实的一天哇。

还充实巩固了RMQ

    for (int j=1;j<=20;j++)
        for (int i=1;i+(1<<j)-1<=n;i++)
            f1[i][j]=max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]);
    
原文地址:https://www.cnblogs.com/Hathawaxy/p/9268907.html