/******************************************************************/
以下题解来自互联网:Juny的博客
思路核心:给你的闭包其实就是一个有向图;
方法:
1,对此图进行缩点,对于点数为n(n>1)的强连通分量最少要 n 条边,
对点数为 1 的强连通不需要边,这样计算出边数 m1 ;
2,在缩点后的有向无环图中进行反floyd,如果有边a->b,b->c,a->c那么显然a->c可以去掉,
就这样一直去除这样的边,直到不能再去为止,算出最终边数 m2;
3,m1+m2 即为答案;
这样做速度比较慢,但小草还没想出其他好的办法,希望有大牛指点……
/*****************************************************************/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std; #define MAXN 20010
#define MAXM 50010 struct Edge
{
int v, next;
}edge[MAXM]; //边结点数组 int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,
//Belong[]为每个结点所对应的强连通分量标号数组
// Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号
int instack[MAXM],num[MAXN]; // instack[]为是否在栈中的标记数组
int n, m, cnt, scnt, top, tot; void init()
{
cnt = ;
scnt = top = tot = ;
memset(first, -, sizeof(first));
memset(DFN, , sizeof(DFN));
memset(num,,sizeof(num));
} void read_graph(int u, int v)
{
edge[tot].v = v;
edge[tot].next = first[u];
first[u] = tot++;
} void Tarjan(int v)
{
int t;
DFN[v] = Low[v] = ++cnt;
instack[v] = ;
stack[top++] = v;
for(int e = first[v]; e != -; e = edge[e].next)
{
int j = edge[e].v;
if(!DFN[j])
{
Tarjan(j);
if(Low[v] > Low[j]) Low[v] = Low[j]; }
else if(instack[j] && DFN[j] < Low[v])
{
Low[v] = DFN[j];
}
}
if(DFN[v] == Low[v])
{
scnt++;
do
{
t = stack[--top];
instack[t] = ;
Belong[t] = scnt; //为缩点做准备的
num[scnt]++;
}while(t != v);
}
} void solve()
{
for(int i = ; i <= n; i++)
if(!DFN[i])
Tarjan(i);
} int main()
{
int i,j,map[][],sum1,ans,map1[][];//map1[][]是缩点后新建的图
while(scanf("%d",&n)!=EOF)
{
init();
ans=;
memset(map,,sizeof(map));
memset(map1,,sizeof(map1));
for(i=;i<=n;i++)
{
for(j=;j<=n;j++)
{
scanf("%d",&map[i][j]);
if(map[i][j]==&&i!=j)
{
read_graph(i,j);
}
}
}
solve();
sum1=;
for(i=;i<=scnt;i++)
{
if(num[i]>)
sum1+=num[i];
}
for(int ii=;ii<=n;ii++)
{
for(int jj=;jj<=n;jj++)
{
if(map[ii][jj]&&Belong[ii]!=Belong[jj])
map1[Belong[ii]][Belong[jj]]=;
}
}
for(int ii=;ii<=scnt;ii++)
for(int jj=;jj<=scnt;jj++)
for(int kk=;kk<=scnt;kk++)
if(map1[ii][jj]&&map1[ii][kk]&&map1[kk][jj])//此处在缩点新建图
map1[ii][jj]=;
for(int ii=;ii<=scnt;ii++)
for(int jj=;jj<=scnt;jj++)
if(map1[ii][jj])
ans++;
printf("%d\n",sum1+ans);
}
return ;
}