题面
https://www.luogu.org/problem/P4306
题解
注意到一个强连通分量中的点是互相可达的,缩点形成的$DAG$的后继也是可达的,所以想到$dp$,但是因为不同的后继可能到达相同的后后继,所以用$bitset$判重。
要不是用了$bitset$我才不写呢。
注意$bitset$中,统计有多少个$1$用的是$b.count()$,并不是size。
#include<stack> #include<bitset> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #define ri register int #define N 2050 using namespace std; char s[N][N]; int n,dfn[N],low[N],cnt=0,clo,bel[N],siz[N]; bool ins[N],vis[N]; stack<int> sta; vector<int> to[N],about[N]; bitset<N> ans[N]; void tarjan(int x) { dfn[x]=low[x]=++clo; sta.push(x); ins[x]=1; for (ri i=0;i<to[x].size();++i) { int y=to[x][i]; if (dfn[y]) { if (ins[y]) low[x]=min(low[x],dfn[y]); } else { tarjan(y); low[x]=min(low[x],low[y]); } } if (low[x]==dfn[x]) { ++cnt; while (1) { int t=sta.top(); sta.pop(); bel[t]=cnt; ins[t]=0; siz[cnt]++; ans[cnt][t]=1; if (t==x) break; } } } int main() { scanf("%d",&n); for (ri i=1;i<=n;i++) { scanf("%s",s[i]+1); } for (ri i=1;i<=n;i++) { for (ri j=1;j<=n;j++) if (s[i][j]==‘1‘) to[i].push_back(j); } for (ri i=1;i<=n;i++) if (!dfn[i]) tarjan(i); for (ri i=1;i<=n;i++) { for (ri j=1;j<=n;j++) if (s[i][j]==‘1‘ && bel[i]!=bel[j]) about[bel[i]].push_back(bel[j]); } int sum=0; for (ri i=1;i<=cnt;i++) { for (ri j=0;j<about[i].size();j++) { ans[i]|=ans[about[i][j]]; } sum+=siz[i]*ans[i].count(); } printf("%d\n",sum); }