• 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. }  
  • 相关阅读:
    新的工作电脑 神州优雅A550i7
    职责链模式(Chain of Responsibility)
    访问者模式(Visitor)
    单例模式(Singleton)
    解释器模式(Interpreter)
    迭代器模式(Iterator)
    解决 Visual Stuido 工具栏中的工具重置后,恢复回来。
    WCF 一步一步 发布 WCF服务 到 Windows 服务 (图)
    桥接模式(Bridge)与合成/聚合复用原则(CARP)
    dbentry访问带密码的Access
  • 原文地址:https://www.cnblogs.com/TY02/p/11112109.html
Copyright © 2020-2023  润新知