1. 程式人生 > >【BZOJ2208/Jsoi2010】連通數

【BZOJ2208/Jsoi2010】連通數

                                        2208: [Jsoi2010]連通數

                                                                  Time Limit: 20 Sec  Memory Limit: 512 MB                                                                               Submit: 4160  Solved: 1794

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。

解析:

       這道題資料是真的水而且做法也很多。。。

       最先看到這道題感覺資料範圍挺小的,感覺暴力(實際上最壞O(N^3))沒問題,結果還真能過。。。

       但是我們不能僅僅滿足於AC對吧,所以思考一下正解。

       首先強連通縮點,重新簡圖,於是答案就為:

       ans=\sum_{i=1}^{n}\sum_{j=1}^{n}sum[i]*sum[j]

       前提條件是連通塊i能到達於連通塊j

       如何算出連通塊之間的連通性?直接用Floyd傳遞閉包,但是最好用bitset

壓位能跑得更快。

      當然也有人縮完點後用拓撲序遞推複雜度應該更優秀一些,還有人不縮點直接用bitset壓位跑Floyd也能過而且速度也不慢。。。

搜尋(最慢但最好想):

#include <bits/stdc++.h>
using namespace std;
 
const int Max=2010;
int n,m,ans,size;
int first[Max],vis[Max];
char ch[Max];
struct shu{int to,next;};
shu edge[Max*Max];
 
inline int get_int()
{
    int x=0,f=1;
    char c;
    for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
    if(c=='-') f=-1,c=getchar();
    for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return x*f;
}
 
inline void build(int x,int y)
{
    edge[++size].next=first[x];
    first[x]=size;
    edge[size].to=y;
}
 
inline void dfs(int point)
{
    if(!vis[point]) ans++,vis[point]=1;
    for(int u=first[point];u;u=edge[u].next)
    {
      int to=edge[u].to;
      if(vis[to]) continue;
      dfs(to);
    }
}
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
      scanf("%s",ch+1);
      for(int j=1;j<=n;j++) if(ch[j]=='1') build(i,j);
    }
    for(int i=1;i<=n;i++)
    {
      dfs(i);
      memset(vis,0,sizeof(vis));
    }
    cout<<ans<<"\n";
    return 0;
}

直接bitset壓位Floyd(最短速度適中):

//次程式碼源自https://blog.csdn.net/linkfqy/article/details/75578669
#include<cstdio>
#include<bitset>
using namespace std;
const int maxn=2005;
int n,ans;
char s[maxn];
bitset<maxn> f[maxn];
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%s",s);
        for (int j=1;j<=n;j++)
         if (s[j-1]=='1'||i==j) f[i][j]=1;
    }
    for (int i=1;i<=n;i++)
     for (int j=1;j<=n;j++)
      if (f[j].test(i)) f[j]|=f[i];
    for (int i=1;i<=n;i++) ans+=f[i].count();
    printf("%d",ans);
    return 0;
}

縮點+bitset壓位Floyd(速度較快):

#include <bits/stdc++.h>
using namespace std;
 
const int Max=2005;
int n,m,size,cnt,tot,Index,ans;
int first[Max],low[Max],num[Max],p[Max],vis[Max],sum[Max],father[Max];
char ch[Max];
struct shu{int to,next;};
shu edge[Max*Max];
bitset<2005>f[Max];
 
inline int get_int()
{
    int x=0,f=1;
    char c;
    for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
    if(c=='-') f=-1,c=getchar();
    for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return x*f;
}
 
inline void build(int x,int y)
{
    edge[++size].next=first[x];
    first[x]=size;
    edge[size].to=y;
}
 
inline void tarjan(int point)
{
    num[point]=low[point]=++Index;
    p[++tot]=point,vis[point]=1;
    for(int u=first[point];u;u=edge[u].next)
    {
      int to=edge[u].to;
      if(!num[to]) tarjan(to),low[point]=min(low[point],low[to]);
      else if(vis[to]) low[point]=min(num[to],low[point]);
    }
    if(low[point]==num[point])
    {
      cnt++;
      while(1)
      {
        int x=p[tot--];
        father[x]=cnt,vis[x]=0,sum[cnt]++;
        if(x==point) break;
      }
    }
}                  
 
inline void solve()
{
    size=0;
    for(int i=1;i<=cnt;i++) f[i][i]=1;
    for(int i=1;i<=n;i++)
      for(int u=first[i];u;u=edge[u].next)
         f[father[i]][father[edge[u].to]]=1;
    for(int i=1;i<=cnt;i++)
      for(int j=1;j<=cnt;j++)
        if(f[i][j]) f[i]|=f[j];
    for(int i=1;i<=cnt;i++)
      for(int j=1;j<=cnt;j++)
        if(f[i][j]) ans+=sum[i]*sum[j];
}
 
int main()
{
    n=get_int();
    for(int i=1;i<=n;i++)
    {
      scanf("%s",ch+1);
      for(int j=1;j<=n;j++) if(ch[j]=='1') build(i,j);
    }
    for(int i=1;i<=n;i++) if(!num[i]) tarjan(i);
    solve();
    cout<<ans<<"\n";
    return 0;
}