Luogu P4306 JSOI2010 连通数

  tarjan有向图缩点的基础应用。把原图中某点的连通数转化为反向图中”能够到达某点的个数“。缩点后,每个新点的贡献等于

  原dcc大小 * f[i]

  其中f[i]表示(包括该点自身)通向该点的点的个数。设u点为v的入度,满足转移方程:

    

  所以我们按照拓扑序dp求解即可。f[i]的初值设为该分量的节点数。

  这个题引出一个很重要的想法:如何避免两个强连通分量缩点时连有重边?对于2000的数据范围,一个二维布尔数组完全可以承受,但显然有更普适的优秀做法,这就是Hash。去重边实际上是二元组的判重问题,我们只需要一个合适的“进位”技术,就可以保证任意两个二元组所映射的键值是绝不相同的。如果key值太大,就要套用Hash表解决了。

  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <queue>  
  5. #include <cctype>  
  6. #define maxn 2010  
  7. using namespace std;  
  8. template <typename T>  
  9. void read(T &x) {  
  10.     x = 0;  
  11.     char ch = getchar();  
  12.     while (!isdigit(ch))  
  13.         ch = getchar();  
  14.     while (isdigit(ch))  
  15.         x = x * 10 + (ch ^ 48),   
  16.         ch = getchar();  
  17.     return;  
  18. }  
  19. struct E {  
  20.     int to, nxt;  
  21. } edge[maxn * maxn], edge2[maxn * maxn];  
  22. int n, head[maxn], top, head2[maxn], top2;  
  23. inline void insert(int u, int v) {  
  24.     edge[++top] = (E) {v, head[u]};  
  25.     head[u] = top;  
  26. }  
  27. inline void insert2(int u, int v) {  
  28.     edge2[++top2] = (E) {v, head2[u]};  
  29.     head2[u] = top2;  
  30. }  
  31. int dfn[maxn], low[maxn], timer,  
  32. sta[maxn], stp,   
  33. c[maxn], cnt,   
  34. w[maxn];  
  35. bool ins[maxn];  
  36. void tarjan_dfs(int u) {  
  37.     dfn[u] = low[u] = ++timer;  
  38.     sta[++stp] = u, ins[u] = true;  
  39.     for (int i = head[u]; i; i = edge[i].nxt) {  
  40.         int v = edge[i].to;  
  41.         if (!dfn[v])  
  42.             tarjan_dfs(v), low[u] = min(low[u], low[v]);  
  43.         else if (ins[v])  
  44.             low[u] = min(low[u], dfn[v]);  
  45.     }  
  46.     if (dfn[u] == low[u]) {  
  47.         ++cnt;  
  48.         int x;  
  49.         do {  
  50.             x = sta[stp--];  
  51.             ins[x] = false;  
  52.             c[x] = cnt;  
  53.             ++w[cnt];  
  54.         } while (x != u);  
  55.     }  
  56. }  
  57. void tarjan() {  
  58.     for (int i = 1; i <= n; ++i)   
  59.         if (!dfn[i]) tarjan_dfs(i);  
  60. }  
  61. namespace Hash_table {  
  62. //  const int Size(23333309), step = 7;//空间足够,不用取模   
  63.     bool tb[4004001];   
  64.     inline int H (int u, int v) {  
  65.         return u * 2001 + v;  
  66.     }  
  67.     bool Hash(int u, int v) {  
  68.         int key = H(u, v);  
  69.         if (tb[key]) return false;  
  70.         tb[key] = true;  
  71.         return true;  
  72.     }  
  73. using namespace Hash_table;  
  74. int ind[maxn];  
  75. //bool done[maxn][maxn];//Hash更为优秀
  76. void build() {  
  77.     for (int u = 1; u <= n; ++u)   
  78.         for (int i = head[u]; i; i = edge[i].nxt) {  
  79.             int v = edge[i].to;  
  80.             if (c[u] != c[v] && Hash(c[u], c[v])) {  
  81.                 insert2(c[u], c[v]);  
  82.                 ++ind[c[v]];  
  83.             }  
  84.         }  
  85. }  
  86. long long sum = 0;  
  87. long long f[maxn];  
  88. queue<int> que;  
  89. void dp() {  
  90.     for (int i = 1; i <= cnt; ++i) {  
  91.         f[i] = w[i];  
  92.         if (!ind[i]) {  
  93.             sum += w[i] * w[i];  
  94.             que.push(i);  
  95.         }  
  96.     }  
  97.     while (!que.empty()) {  
  98.         int u = que.front(); que.pop();  
  99.         for (int i = head2[u]; i; i = edge2[i].nxt) {  
  100.             int v = edge2[i].to;  
  101.             f[v] += f[u];   
  102.             --ind[v];  
  103.             if (!ind[v]) {  
  104.                 sum += w[v] * f[v];  
  105.                 que.push(v);  
  106.             }  
  107.         }  
  108.     }  
  109. }  
  110. int main() {  
  111.     read(n);  
  112.     for (int u = 1; u <= n; ++u)  
  113.         for (int v = 1; v <= n; ++v) {  
  114.             char ch = getchar();  
  115.             while (!isdigit(ch)) ch = getchar();  
  116.             if (ch == '1') insert(v, u);//反向存图  
  117.         }  
  118.     tarjan();  
  119.     build();  
  120.     dp();  
  121.     printf("%lld", sum);  
  122.     return 0;  
  123. }  
posted @ 2019-07-01 08:27  onyYuan  阅读(115)  评论(0编辑  收藏  举报