[BZOJ 2208] 连通数
Link:
Solution:
传递闭包模板题
传递闭包是集合中最小的二元关系,其实就是对二元关系的不断拓展,一般用$floyd$求解
这里要先跑一遍$tarjan$求出$SCC$,然后在$TopoSort$时顺便传递闭包,最后统计答案即可
Tips:
1、在$DAG$上递推使用拓扑排序更高效,直接$dfs$时间复杂度没有保证,很可能$TLE$
2、如需对二进制位统一处理(位运算),使用$bitset$,能通过压位快32倍!
3、这题$O(n*m)$和$O(\frac{n^3}{32})$的纯暴力都能过……
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=2005; ll res=0; bool vis[MAXN],instack[MAXN]; vector<int> G[MAXN];char dat[MAXN][MAXN]; int n,in[MAXN],low[MAXN],dfn[MAXN],col[MAXN],sum[MAXN],tp=0,cnt=0; stack<int> s; bitset<MAXN> f[MAXN]; void tarjan(int x) { dfn[x]=low[x]=++tp; vis[x]=instack[x]=true; s.push(x); for(int i=0;i<G[x].size();i++) { int v=G[x][i]; if(instack[v]) low[x]=min(low[x],low[v]); else if(!vis[v]) tarjan(v),low[x]=min(low[x],low[v]); } if(low[x]==dfn[x]) { int tmp=-1;cnt++; while(tmp!=x) { tmp=s.top();s.pop(); instack[tmp]=false; col[tmp]=cnt;sum[cnt]++; } } } void Topo_sort() { for(int i=1;i<=cnt;i++) f[i][i]=1; queue<int> q; for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i); while(!q.empty()) { int u=q.front();q.pop(); for(int i=0;i<G[u].size();i++) { int v=G[u][i];f[v]|=f[u]; if(!(--in[v])) q.push(v); } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",dat[i]+1); for(int j=1;j<=n;j++) if(dat[i][j]=='1') G[i].push_back(j); } for(int i=1;i<=n;i++) if(!vis[i]) tarjan(i); for(int i=0;i<MAXN;i++) G[i].clear(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(dat[i][j]=='1' && col[i]!=col[j]) G[col[j]].push_back(col[i]),in[col[i]]++; Topo_sort(); for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) if(f[i][j]) res+=1ll*sum[i]*sum[j]; printf("%lld",res); return 0; }