BZOJ 2208: [Jsoi2010]连通数 Tarjan
2208: [Jsoi2010]连通数
Description
Input
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
Output
输出一行一个整数,表示该图的连通数。
Sample Input
3
010
001
100
010
001
100
Sample Output
9
思路 :
tarjan + 拓扑排序 + dp + STL:: bitset 优化 f[i][j]表示从i到j这种状态的答案, 把jbitset压一个32就可以过了
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <stack> #include <bitset> #include <queue> using namespace std; const int N = 2100, M = 4001000; int head[N], to[M], nxt[M], cnt; int dfn[N], low[N], place[N], val[N], idx, tot; int map[N][N]; bool vis[N][N]; int ind[N]; stack<int>s; bitset<N> f[N]; int ins[N]; void add(int x, int y) { to[++cnt] = y; nxt[cnt] = head[x]; head[x] = cnt; } void Tarjan(int p) { dfn[p] = low[p] = ++idx; ins[p] = 1;s.push(p); for(int i=head[p];i;i=nxt[i]) { if(!dfn[to[i]]) { Tarjan(to[i]); low[p] = min(low[p], low[to[i]]); } else if(ins[to[i]]) { low[p] = min(low[p], dfn[to[i]]); } } if(dfn[p]==low[p]) { int t = 0; tot++; while(t!=p) { t = s.top();s.pop(); ins[t] = 0; place[t] = tot; val[tot]++; } } } int n; void rebuild() { memset(head, 0, sizeof(head)); cnt = 0; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(map[i][j]&&place[i]!=place[j]) ind[place[i]]++,add(place[j], place[i]); } } for(int i=1;i<=tot;i++) f[i][i]=1; } int ans = 0; void topo() { queue<int>q; for(int i=1;i<=tot;i++) if(!ind[i]) q.push(i); while(!q.empty()) { int u = q.front();q.pop(); for(int i=head[u];i;i=nxt[i]) { ind[to[i]]--; f[to[i]]|=f[u]; if(!ind[to[i]])q.push(to[i]); } } for(int i=1;i<=tot;i++) { for(int j=1;j<=tot;j++) { if(f[i][j]) ans += val[i] * val[j]; } } } int main() { scanf("%d", &n); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%1d", &map[i][j]); if(map[i][j]) add(i, j); } } for(int i=1;i<=n;i++) { if(!dfn[i]) Tarjan(i); } rebuild(); topo(); printf("%d\n", ans); }