题意
给出一个有向图,求有多少个点对(i,j)满足i可以到达j(i可以等于j)
分析
这题据说暴力也能卡过去,毕竟20s的时限我才跑了1s+,但打暴力的话就没意思了,所以就果断码了一波正解。
这题一开始也有一点大概的思路也就是用bitset之类的状态压缩来搞一搞,但是没想到用tarjan来缩点。
那么正解其实就是先用tarjan来缩一下点,那么每个强连通分量对ans的贡献就是size*size,然后拓扑排序一下,再用bitset来记录每个点能到达那些点就好了。
总结
以后碰到求有向图无向图求连通数的时候要记得用tarjan缩点。
第一次用bitset
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<stack>
#define N 2005
using namespace std;
bitset <N> vis[N];
stack <int> q;
int low[N],dfn[N],bel[N],size[N],map[N][N],map1[N][N],n,tot,sum,ans,du[N],f[N];
char str[N];
void tarjan(int x)
{
dfn[x]=++tot;
low[x]=tot;
q.push(x);
f[x]=1;
for (int i=1;i<=n;i++)
if (map[x][i]&&!dfn[i])
{
tarjan(i);
low[x]=min(low[x],low[i]);
}
else if (map[x][i]&&f[i]) low[x]=min(low[x],low[i]);
if (low[x]==dfn[x])
{
sum++;
while (1)
{
int j=q.top();
q.pop();
f[j]=0;
bel[j]=sum;
size[sum]++;
vis[sum][j]=true;
if (j==x) break;
}
ans+=size[sum]*size[sum];
}
}
stack <int> q1;
void topsort()
{
while (!q.empty()) q.pop();
for (int i=1;i<=sum;i++)
if (!du[i])
{
q.push(i);
q1.push(i);
}
while (!q1.empty())
{
int x=q1.top();
q1.pop();
for (int i=1;i<=sum;i++)
if (map1[x][i])
{
du[i]--;
if (!du[i])
{
q.push(i);
q1.push(i);
}
}
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",str);
for (int j=0;j<n;j++)
if (str[j]=='1') map[i][j+1]=1;
}
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (map[i][j]&&!map1[bel[i]][bel[j]]&&bel[i]!=bel[j])
{
map1[bel[i]][bel[j]]=1;
du[bel[j]]++;
}
topsort();
while (!q.empty())
{
int x=q.top();
q.pop();
bitset <N> t;
for (int i=1;i<=sum;i++)
if (map1[x][i]) t|=vis[i];
ans+=t.count()*size[x];
vis[x]|=t;
}
printf("%d",ans);
return 0;
}