bzoj2208连通数
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2208
Description
Input
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
Output
输出一行一个整数,表示该图的连通数。
Sample Input
3
010
001
100
010
001
100
Sample Output
9
HINT
对于100%的数据,N不超过2000。
题解:
先tarjan缩点,然后对于两个连通的连通块,显然其对答案的贡献为sum[i]*sum[j],sum为块中节点个数
代码:
#include<iostream> #include<cstdlib> #include<cstdio> #include<queue> #include<algorithm> #define N 2010 using namespace std; int head[N],next[N*N],to[N*N]; int Head[N],Next[N*N],To[N*N]; int dfn[N],low[N],belong[N],c[N],s[N],num[N]; int sum,p,q,g,top,n,f,ans; bool vis[N]; char S[N]; queue<int>Q; void link(int x,int y) {next[++sum]=head[x]; head[x]=sum; to[sum]=y;} void Link(int x,int y) {Next[++sum]=Head[x]; Head[x]=sum; To[sum]=y;} void tarjan(int p) { dfn[p]=low[p]=++sum; s[++top]=p; vis[p]=true; for (int i=head[p];i;i=next[i]) { int v=to[i]; if (!dfn[v]) {tarjan(v); low[p]=min(low[p],low[v]);} else if (vis[v]) low[p]=min(low[p],dfn[v]); } if (dfn[p]==low[p]) { do { q=s[top--]; vis[q]=false; belong[q]=p; num[p]++; } while(p!=q); ans+=num[p]*num[p]; } } void dfs(int p) { for (int j=Head[p];j;j=Next[j]) if (!c[To[j]]) Q.push(To[j]),c[To[j]]=1,ans+=f*num[To[j]],dfs(To[j]); } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%s",S); for (int j=0;j<=n-1;j++) if (S[j]=='1') link(i,j+1); } sum=0; for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i); sum=0; for (int i=1;i<=n;i++) for (int j=head[i];j;j=next[j]) if (belong[i]!=belong[to[j]]) Link(belong[i],belong[to[j]]); for (int i=1;i<=n;i++) if (belong[i]==i) { while (!Q.empty()) {g=Q.front(); c[g]=0; Q.pop();} f=num[i]; dfs(i); } printf("%d\n",ans); return 0; }
转载请联系博主!!!