Description
对于100%的数据,N不超过2000。
Solution
容易想到要tarjan缩点按拓扑序递推,但是去重的步骤不好弄
C++STL中有bitset,用这个当成二进制按位状压即可
一开始错是没有给每个连通分量标记自己包含的点,这样就只算了相同连通分量的贡献。奈何拍的数据太弱,浪费了1h
Code
#include <stdio.h>
#include <string.h>
#include <bitset>
#include <stack>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define min(x,y) ((x)<(y)?(x):(y))
const int N=2025;
const int E=4000005;
std:: stack<int> stack;
std:: bitset<N> vis[N];
struct edge{int x,y,next;}e[E];
bool inStack[N];
int queue[N],head,tail;
int dfn[N],low[N],scc[N];
int ls[N],d[N],n,edCnt=0;
int size[N];
char str[N];
void addEdge(int x,int y) {
e[++edCnt]=(edge){x,y,ls[x]};
ls[x]=edCnt; d[y]++;
}
void dfs(int now) {
dfn[now]=low[now]=++dfn[0];
stack.push(now); inStack[now]=1;
for (int i=ls[now];i;i=e[i].next) {
if (!dfn[e[i].y]) {
dfs(e[i].y);
low[now]=min(low[now],low[e[i].y]);
} else if (dfn[e[i].y]&&inStack[e[i].y]) low[now]=min(low[now],dfn[e[i].y]);
}
if (dfn[now]==low[now]) {
scc[0]++;
for (int tmp=0;tmp!=now;) {
tmp=stack.top(); stack.pop();
inStack[tmp]=0;
scc[tmp]=scc[0];
size[scc[0]]++;
vis[scc[0]][tmp]=1;
}
}
}
void tarjan() {
fill(dfn,0); fill(low,0);
fill(inStack,0);
rep(i,1,n)
if (!dfn[i]) dfs(i);
int tmp=edCnt;
fill(ls,0); fill(d,0);
rep(i,1,tmp) if (scc[e[i].x]!=scc[e[i].y]) {
addEdge(scc[e[i].x],scc[e[i].y]);
}
}
void top_sort() {
head=1,tail=0;
rep(i,1,scc[0]) if (!d[i]) {
queue[++tail]=i;
}
int ans=0;
while (head<=tail) {
int now=queue[head++];
ans+=size[now]*size[now];
for (int i=ls[now];i;i=e[i].next) {
if (!(--d[e[i].y])) queue[++tail]=e[i].y;
}
}
drp(ti,scc[0],1) {
int now=queue[ti];
for (int i=ls[now];i;i=e[i].next) {
vis[now]|=vis[e[i].y];
}
}
rep(i,1,scc[0]) ans+=(vis[i].count()-size[i])*size[i];
printf("%d\n", ans);
}
int main(void) {
// freopen("data.in","r",stdin);
// freopen("myp.out","w",stdout);
scanf("%d",&n);
rep(i,1,n) {
scanf("%s",str);
rep(j,1,n) if (str[j-1]=='1') addEdge(i,j);
}
tarjan();
top_sort();
return 0;
}