bzoj2208 [Jsoi2010]连通数(scc+bitset)

时间:2021-01-19 15:16:43

2208: [Jsoi2010]连通数

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1879  Solved: 778
[Submit][Status][Discuss]

Description

bzoj2208 [Jsoi2010]连通数(scc+bitset)

Input

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

Output

输出一行一个整数,表示该图的连通数。

Sample Input

3
010
001
100

Sample Output

9

HINT

对于100%的数据,N不超过2000。

Source

【思路】

强连通分量+bitset传递闭包。

求出scc,缩点。对于新的DAG,如果两个scc之间相连则ans+=sccsz[i]*sccsz[j]。利用bitset与递推判断相连。

【代码】

 #include<cstdio>
#include<bitset>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std; const int maxn = +; int pre[maxn],lowlink[maxn],sccno[maxn],sccsz[maxn],dfs_clock,scc_cnt;
vector<int> G[maxn];
stack<int> S; int dfs(int u) {
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(int i=;i<G[u].size();i++) {
int v=G[u][i];
if(!pre[v]) {
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}
else if(!sccno[v]) {
lowlink[u]=min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u]) {
scc_cnt++;
for(;;) {
int x=S.top(); S.pop();
sccno[x]=scc_cnt;
sccsz[scc_cnt]++;
if(x==u) break;
}
}
}
void find_scc(int n) {
memset(pre,,sizeof(pre));
memset(sccsz,,sizeof(sccsz));
memset(sccno,,sizeof(sccno));
scc_cnt=dfs_clock=;
for(int i=;i<n;i++)
if(!pre[i]) dfs(i);
} bitset<maxn> f[maxn]; //使用bitset
int n,in[maxn];
char s[maxn]; vector<int> Gx[maxn];
void rebuild() {
for(int i=;i<=scc_cnt;i++) in[i]=;
for(int i=;i<n;i++) {
int u=sccno[i];
for(int j=;j<G[i].size();j++) {
int v=sccno[G[i][j]];
if(u!=v) Gx[u].push_back(v),in[v]++;
}
}
} queue<int> q;
void solve() {
for(int i=;i<=scc_cnt;i++) f[i][i]=;
for(int i=;i<=scc_cnt;i++) if(!in[i]) q.push(i);
while(!q.empty()) {
int u=q.front(); q.pop();
for(int i=;i<Gx[u].size();i++) {
int v=Gx[u][i];
f[v]|=f[u]; //传递闭包
if((--in[v])==) q.push(v);
}
}
} int main() {
scanf("%d",&n);
for(int i=;i<n;i++){
scanf("%s",s);
for(int j=;j<n;j++) {
if(s[j]-'') G[i].push_back(j);
}
}
find_scc(n);
rebuild();
solve();
int ans=;
for(int i=;i<=scc_cnt;i++)
{
for(int j=;j<=scc_cnt;j++)
if(f[i][j]) ans+=sccsz[i]*sccsz[j];
}
printf("%d\n",ans);
return ;
}