Luogu P2656 采蘑菇

尽管是缩点的习题,思路也是在看了题解后才明白的。

首先,每个强连通分量内的点都是一定互通的,也就是可以完全把这里面的边都跑满,摘掉所有能摘的蘑菇。那么,考虑给每一个强连通分量化为的新点一个点权,代表摘光蘑菇能拿到的边权之和。然后,在新点之间保留原来的桥及其初始权值。(每一个桥一定只能跑一遍,否则说明这两个本应单向通行的分量之间有返回的路径,则二者可构成一个更大的分量。这个结论正是tarjan算法求有向图dcc的核心原理。)现在得到了一张新图,问题在于如何在一张包含点权、边权的DAG上求起始于定点的最长路。

这个问题可以用拓扑序DP求解。在dp求最长路的基础上,为了保证一定由s点所在分量起始,我们把该分量初状态设为其权值,其余点都赋初值为INT_MIN。

这样dp得到的最长路一定是基于f[dcc[s]]求出的。

另外,用SPFA算法来跑点权、边权交叉的最长路是可行的,不过应用于本题复杂度不如dp优秀。

-----------------------------------------

在参阅题解后,基于一开始跑偏的假设,笔者又想到了一个貌似更优的解法。

实际上我们并不需要考虑原图的所有节点。容易想到,从给定起点向外作一轮tarjan算法(dfs)不能达到的点,在新图中也不可能走到。因此,我们只需要对图中以s为原点作一次tarjan能够跑到的几个连通分量进行缩点,这样能够到达的区域就变成了一棵以s为根的树。我们只需要再作一次dfs求出最深叶节点的深度即可。

(注:以下代码注释分部分为除最后一种思路的其余解法,仅供参考)

  1. #include <cstdio>  
  2. #include <iostream>  
  3. #include <queue>  
  4. #include <climits>  
  5. #define rint register int  
  6. #define BUG putchar('*')  
  7. #define maxn 80010  
  8. #define maxm 200010  
  9. using namespace std;  
  10.   
  11. struct E {  
  12.     int to, nxt, w;  
  13.     double op;  
  14. } edge[maxm], edge2[maxm];  
  15. int n, m, st;  
  16. int head[maxn], top;  
  17. inline void insert(int u, int v, int w, double op) {  
  18.     edge[++top] = (E) {v, head[u], w, op};  
  19.     head[u] = top;  
  20. }  
  21. int dfn[maxn], low[maxn], sta[maxn], stp, timer;  
  22. bool ins[maxn], vis[maxn];  
  23. int cnt, c[maxn];  
  24. void dfs(int u) {  
  25.     dfn[u] = low[u] = ++timer;  
  26.     sta[++stp] = u;  
  27.     ins[u] = true;  
  28.     vis[u] = true;//  仅搜一次标记所答点
  29.     for (rint i = head[u]; i; i = edge[i].nxt) {  
  30.         int v = edge[i].to;  
  31.         if (!dfn[v]) {  
  32.             dfs(v);  
  33.             low[u] = min(low[u], low[v]);  
  34.         } else if (ins[v])  
  35.             low[u] = min(low[u], dfn[v]);  
  36.     }  
  37.     if (dfn[u] == low[u]) {  
  38.         ++cnt;  
  39.         int x;  
  40.         do {  
  41.             x = sta[stp--];  
  42.             ins[x] = false;  
  43.             c[x] = cnt;  
  44.         } while (x != u);  
  45.     }  
  46. }  
  47. void tarjan() {  
  48. //  for (int i = 1; i <= n; ++i) //  全图tarjan
  49. //      if (!dfn[i]) dfs(i);  
  50.     dfs(st);  
  51. }  
  52. int head2[maxn], top2;  
  53. inline void insert2(int u, int v, int w) {  
  54.     edge2[++top2] = (E) {v, head2[u], w, 0};  
  55.     head2[u] = top2;  
  56. }  
  57. int val[maxn], ind[maxn];  
  58. void build() {  
  59.     rint v, w;  
  60.     for (rint u = 1; u <= n; ++u)  
  61.         if (vis[u])//  仅考虑一次搜索 缩点得树
  62.         for (int i = head[u]; i; i = edge[i].nxt) {  
  63.             v = edge[i].to;  
  64.             w = edge[i].w;  
  65.             if (c[u] == c[v]) {  
  66.                 register double op = edge[i].op;  
  67.                 while (w)   
  68.                     val[c[u]] += w, w *= op;  
  69.             } else   
  70.                 insert2(c[u], c[v], w), ind[c[v]]++;  
  71.         }  
  72. }  
  73. //************************  
  74. /*  DAG 拓扑序dp
  75. int f[maxn];  
  76. queue<int> q;  
  77. int dp() {  
  78.     int ans = val[c[st]];  
  79.     for (int i = 1; i <= cnt; ++i) {  
  80.         f[i] = INT_MIN;  
  81.         if (!ind[i]) q.push(i);  
  82.     }  
  83.     f[c[st]] = val[c[st]];  
  84.     while (!q.empty()) {  
  85.         int u = q.front(); q.pop();  
  86.         for (int i = head2[u]; i; i = edge2[i].nxt) {  
  87.             int v = edge2[i].to;  
  88.             f[v] = max(f[v], f[u] + edge2[i].w + val[v]);  
  89.             --ind[v];  
  90.             if (!ind[v])   
  91.                 ans = max(ans, f[v]), q.push(v);  
  92.         }  
  93.     }  
  94.     return ans;  
  95. }  
  96. */  
  97. //**************************  
  98. /*  spfa
  99. bool inq[maxn];  
  100. int dist[maxn];  
  101. int spfa() {  
  102.     for (int i = 1; i <= cnt; ++i)  
  103.         dist[i] = INT_MIN;  
  104.     dist[c[st]] = val[c[st]];  
  105.     queue<int> q;  
  106.     inq[c[st]] = true, q.push(c[st]);  
  107.     while (!q.empty()) {  
  108.         int u = q.front();  
  109.         q.pop(), inq[u] = false;  
  110.         for (int i = head2[u]; i; i = edge2[i].nxt) {  
  111.             int v = edge2[i].to;  
  112.             if (dist[v] < dist[u] + edge2[i].w + val[v]) {  
  113.                 dist[v] = dist[u] + edge2[i].w + val[v];  
  114.                 if (!inq[v])  
  115.                     q.push(v), inq[v] = true;  
  116.             }  
  117.         }  
  118.     }  
  119.     int ans = 0;  
  120.     for (int i = 1; i <= cnt; ++i)  
  121.         ans = max(ans, dist[i]);  
  122.     return ans;  
  123. }*/  
  124. //***************************  
  125. int ans;  
  126. void dfs2(int u, int dist) {  
  127.     dist += val[u];  
  128.     if (!head2[u]) {  
  129.         ans = max(ans, dist);  
  130.         return;  
  131.     }  
  132.     for (int i = head2[u]; i; i = edge2[i].nxt)  
  133.         dfs2(edge2[i].to, dist + edge2[i].w);  
  134. }  
  135. int main() {  
  136.     scanf("%d %d", &n, &m);  
  137.     int u, v, w;  
  138.     double op;  
  139.     for (rint i = 1; i <= m; ++i) {  
  140.         scanf("%d %d %d %lf", &u, &v, &w, &op);  
  141.         insert(u, v, w, op);  
  142.     }  
  143.     scanf("%d", &st);  
  144.     tarjan();  
  145.     build();  
  146. //  printf("%d", spfa());  
  147. //  printf("%d", dp());  
  148.     dfs2(c[st], 0);  
  149.     printf("%d", ans);  
  150.     return 0;  
  151. }  

猜你喜欢

转载自www.cnblogs.com/TY02/p/11107925.html