Luogu P2656 采蘑菇

Posted ty02

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.  

 

以上是关于Luogu P2656 采蘑菇的主要内容,如果未能解决你的问题,请参考以下文章

P2656 采蘑菇

P2656 采蘑菇

P2656 采蘑菇 - Tarjan缩点+SPFA

[Luogu2656]采蘑菇

[2019CSP-S赛前训练][CF894E] Ralph and Mushrooms

树形DP