题意:求一个有向图中可达顶点对数(每个点可达其自身)
题解:听说可以直接用floyd传递闭包+bitset,但是为了提高效率顺便复习算法,还是采用tarjan先缩点然后反向建图在DAG上一边拓扑排序一边用bitset传递可达点集。
去年的bitset今年终于会用了......
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
using namespace std;
const int N=2000+4;
int n;
int low[N],dfn[N],tim=0,snt=0,bel[N];
int head[N],etot=0;
struct EDGE {
int v,nxt;
}e[N*N];
vector<int > G[N];
int siz[N],ind[N];
inline void adde(int u,int v) {
e[++etot].nxt=head[u],e[etot].v=v,head[u]=etot;
}
stack<int > S;
bitset<N> b[N];
void dfs(int p) {
low[p]=dfn[p]=++tim;
S.push(p);
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (!dfn[v]) {
dfs(v);
low[p]=min(low[p],low[v]);
}
else if (!bel[v]) {
low[p]=min(low[p],dfn[v]);
}
}
if (dfn[p]==low[p]) {
++snt;
while (1) {
int t=S.top();
S.pop();
bel[t]=snt;
++siz[snt];
if (t==p) break;
}
}
}
inline void tarjan() {
for (int i=1;i<=n;++i)
if (!dfn[i]) dfs(i);
for (int p=1;p<=n;++p)
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (bel[p]^bel[v])
G[bel[v]].push_back(bel[p]),++ind[bel[p]];
}
}
inline void topo() {
queue<int > q;
while (!q.empty()) q.pop();
for (int i=1;i<=snt;++i) {
if (!ind[i]) q.push(i);
b[i][i]=true;
}
while (!q.empty()) {
int cur=q.front();
q.pop();
for (int i=0;i<G[cur].size();++i) {
int v=G[cur][i];
b[v]|=b[cur];
if (!(--ind[v])) q.push(v);
}
}
}
int main() {
scanf("%d",&n);
memset(head,-1,sizeof(head));
for (int i=1;i<=n;++i) {
char ss[N];
scanf("%s",ss+1);
for (int j=1;j<=n;++j)
if (ss[j]=='1') adde(i,j);
b[i].reset();
}
tarjan();
topo();
int ans=0;
for (int i=1;i<=snt;++i) {
int temp=0;
for (int j=1;j<=snt;++j)
if (b[i][j]) temp+=siz[j];
ans+=temp*siz[i];
}
printf("%d\n",ans);
return 0;
}