缩点+bitset+DP
先缩点。每一块的答案是他能到达的(包括自己)所有块的siz和自己的siz相乘的和,于是对缩点后的拓扑图进行DP即可。
#include<cstdio>
#include<queue>
#include<bitset>
#include<algorithm>
#define N 2005
using namespace std;
bitset<N> f[N];
queue<int> q;
char s[N];
int ecnt=1, belong[N], last[N], last2[N], dfn[N], low[N], t, st[N], top, tot, siz[N], in[N];;
struct edge{int next,to;}e[N*N*2];
void add(int a, int b)
{
e[++ecnt]=(edge){last[a],b};
last[a]=ecnt;
}
void add2(int a, int b)
{
e[++ecnt]=(edge){last2[a],b};
last2[a]=ecnt;
}
void tarjan(int x)
{
st[++top]=x;
low[x]=dfn[x]=++t;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(dfn[y] && !belong[y])low[x]=min(low[x],dfn[y]);//有向图需要记下belong,表示虽然能搜到,但是独立它成一个连通块,所以不能贡献
else if(!dfn[y]){tarjan(y);low[x]=min(low[x],low[y]);}
}
if(dfn[x]==low[x])
{
tot++;
siz[tot]=1;
while(st[top]!=x)
{
siz[tot]++;
belong[st[top]]=tot;
top--;
}
belong[x]=tot;
top--;
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%s",s+1);
for(int j = 1; j <= n; j++)
if(s[j]=='1')
add(i,j);
}
for(int i = 1; i <= n; i++)
if(!dfn[i])
tarjan(i);
for(int x = 1; x <= n; x++)
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(belong[x]!=belong[y])
{
add2(belong[y],belong[x]);
in[belong[x]]++;
}
}
for(int i = 1; i <= tot; i++)
{
if(!in[i])
{
q.push(i);
}
f[i][i]=1;
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i = last2[x]; i; i=e[i].next)
{
int y=e[i].to;
f[y]|=f[x];
in[y]--;
if(!in[y])q.push(y);
}
}
int ans=0;
for(int i = 1; i <= tot; i++)
for(int j = 1; j <= tot; j++)
if(f[i][j])
{
ans+=siz[i]*siz[j];
}
printf("%d\n",ans);
}