[BZOJ2208][Jsoi2010]連通數
阿新 • • 發佈:2018-12-02
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求每個點的聯通數。剛好兩次
///本來跟隊友說我要練思維…結果腦子不行還是練手速來了。
#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;
}