1. 程式人生 > >[BZOJ2208][Jsoi2010]連通數

[BZOJ2208][Jsoi2010]連通數

Time Limit: 20 Sec
Memory Limit: 512 MB

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。

題解:
既然n<=2000那就n方演算法幹過去咯,tarjan縮環重建圖,然後在DAG上跑dfs求每個點的聯通數。剛好兩次 n

2 n^2
///本來跟隊友說我要練思維…結果腦子不行還是練手速來了。

#include<bits/stdc++.h>
#define LiangJiaJun main
#define ll long long
using namespace std;
struct edge{
    int to,nt;
}e[4000004];
int h[2004],n,m,ne;
char mp[2004][2004];
bool inq[2004
],vis[2004]; int cnt,belong[2004],scc,low[2004],dfn[2004],sz[2004],ru[2004]; stack<int>st; void add(int u,int v){ e[++ne].to=v;e[ne].nt=h[u]; h[u]=ne; } void dfs(int x){ dfn[x]=low[x]=++cnt; inq[x]=1; st.push(x); for(int i=1;i<=n;i++){ if(mp[x][i]=='0')continue; if
(!dfn[i]){ dfs(i); low[x]=min(low[x],low[i]); } else if(inq[i]){ low[x]=min(low[x],dfn[i]); } } if(low[x]==dfn[x]){ int now=-1; scc++; while(now!=x){ now=st.top();st.pop(); sz[scc]++; inq[now]=0; belong[now]=scc; } } } void rebuild(){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(mp[i][j]=='0')continue; if(belong[i]!=belong[j]){ add(belong[i],belong[j]); ++ru[belong[j]]; } } } } int f[2004]; int calc(int x){ vis[x]=1; int res=0; for(int i=h[x];i;i=e[i].nt){ if(!vis[e[i].to])res+=calc(e[i].to); } res+=sz[x]; return res; } int w33ha(){ scc=0;cnt=0; memset(ru,0,sizeof(ru)); memset(belong,0,sizeof(belong)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(sz,0,sizeof(sz)); memset(f,0,sizeof(f)); ne=0; memset(h,0,sizeof(h)); for(int i=1;i<=n;i++)scanf("%s",mp[i]+1); for(int i=1;i<=n;i++){ if(!dfn[i]){ dfs(i); } } rebuild(); ll ans=0; for(int i=1;i<=scc;i++){ for(int j=1;j<=scc;j++)vis[j]=0; ans+=1LL*sz[i]*calc(i); } printf("%lld\n",ans); return 0; } int LiangJiaJun(){ while(scanf("%d",&n)!=EOF)w33ha(); return 0; }