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

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

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

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

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

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

实际上我们并不需要考虑原图的所有节点。容易想到,从给定起点向外作一轮tarjan算法(dfs)不能达到的点,在新图中也不可能走到。因此,我们只需要对图中以s为原点作一次tarjan能够跑到的几个连通分量进行缩点,这样能够到达的区域就变成了一棵以s为根的树(8月20日订正:这里的“树”更严谨的说法是“树形图”)。我们只需要再作一次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. struct E {
  11. int to, nxt, w;
  12. double op;
  13. } edge[maxm], edge2[maxm];
  14. int n, m, st;
  15. int head[maxn], top;
  16. inline void insert(int u, int v, int w, double op) {
  17. edge[++top] = (E) {v, head[u], w, op};
  18. head[u] = top;
  19. }
  20. int dfn[maxn], low[maxn], sta[maxn], stp, timer;
  21. bool ins[maxn], vis[maxn];
  22. int cnt, c[maxn];
  23. void dfs(int u) {
  24. dfn[u] = low[u] = ++timer;
  25. sta[++stp] = u;
  26. ins[u] = true;
  27. vis[u] = true;//  仅搜一次标记所答点
  28. for (rint i = head[u]; i; i = edge[i].nxt) {
  29. int v = edge[i].to;
  30. if (!dfn[v]) {
  31. dfs(v);
  32. low[u] = min(low[u], low[v]);
  33. } else if (ins[v])
  34. low[u] = min(low[u], dfn[v]);
  35. }
  36. if (dfn[u] == low[u]) {
  37. ++cnt;
  38. int x;
  39. do {
  40. x = sta[stp--];
  41. ins[x] = false;
  42. c[x] = cnt;
  43. } while (x != u);
  44. }
  45. }
  46. void tarjan() {
  47. //  for (int i = 1; i <= n; ++i) //  全图tarjan
  48. //      if (!dfn[i]) dfs(i);
  49. dfs(st);
  50. }
  51. int head2[maxn], top2;
  52. inline void insert2(int u, int v, int w) {
  53. edge2[++top2] = (E) {v, head2[u], w, 0};
  54. head2[u] = top2;
  55. }
  56. int val[maxn], ind[maxn];
  57. void build() {
  58. rint v, w;
  59. for (rint u = 1; u <= n; ++u)
  60. if (vis[u])//  仅考虑一次搜索 缩点得树
  61. for (int i = head[u]; i; i = edge[i].nxt) {
  62. v = edge[i].to;
  63. w = edge[i].w;
  64. if (c[u] == c[v]) {
  65. register double op = edge[i].op;
  66. while (w)
  67. val[c[u]] += w, w *= op;
  68. } else
  69. insert2(c[u], c[v], w), ind[c[v]]++;
  70. }
  71. }
  72. //************************
  73. /*  DAG 拓扑序dp
  74. int f[maxn];
  75. queue<int> q;
  76. int dp() {
  77. int ans = val[c[st]];
  78. for (int i = 1; i <= cnt; ++i) {
  79. f[i] = INT_MIN;
  80. if (!ind[i]) q.push(i);
  81. }
  82. f[c[st]] = val[c[st]];
  83. while (!q.empty()) {
  84. int u = q.front(); q.pop();
  85. for (int i = head2[u]; i; i = edge2[i].nxt) {
  86. int v = edge2[i].to;
  87. f[v] = max(f[v], f[u] + edge2[i].w + val[v]);
  88. --ind[v];
  89. if (!ind[v])
  90. ans = max(ans, f[v]), q.push(v);
  91. }
  92. }
  93. return ans;
  94. }
  95. */
  96. //**************************
  97. /*  spfa
  98. bool inq[maxn];
  99. int dist[maxn];
  100. int spfa() {
  101. for (int i = 1; i <= cnt; ++i)
  102. dist[i] = INT_MIN;
  103. dist[c[st]] = val[c[st]];
  104. queue<int> q;
  105. inq[c[st]] = true, q.push(c[st]);
  106. while (!q.empty()) {
  107. int u = q.front();
  108. q.pop(), inq[u] = false;
  109. for (int i = head2[u]; i; i = edge2[i].nxt) {
  110. int v = edge2[i].to;
  111. if (dist[v] < dist[u] + edge2[i].w + val[v]) {
  112. dist[v] = dist[u] + edge2[i].w + val[v];
  113. if (!inq[v])
  114. q.push(v), inq[v] = true;
  115. }
  116. }
  117. }
  118. int ans = 0;
  119. for (int i = 1; i <= cnt; ++i)
  120. ans = max(ans, dist[i]);
  121. return ans;
  122. }*/
  123. //***************************
  124. int ans;
  125. void dfs2(int u, int dist) {
  126. dist += val[u];
  127. if (!head2[u]) {
  128. ans = max(ans, dist);
  129. return;
  130. }
  131. for (int i = head2[u]; i; i = edge2[i].nxt)
  132. dfs2(edge2[i].to, dist + edge2[i].w);
  133. }
  134. int main() {
  135. scanf("%d %d", &n, &m);
  136. int u, v, w;
  137. double op;
  138. for (rint i = 1; i <= m; ++i) {
  139. scanf("%d %d %d %lf", &u, &v, &w, &op);
  140. insert(u, v, w, op);
  141. }
  142. scanf("%d", &st);
  143. tarjan();
  144. build();
  145. //  printf("%d", spfa());
  146. //  printf("%d", dp());
  147. dfs2(c[st], 0);
  148. printf("%d", ans);
  149. return 0;
  150. }

这个题最大的收获是发现有向图缩点总跟DAG上的topo+DP有联系。按拓扑序遍历到某一点u,意味u点所有的入点都已经对其完成了更新,此时u点的状态满足无后效性。以及在DAG上求解始于某点的最长路径时,对f数组的特殊处理。

Luogu P2656 采蘑菇的更多相关文章

  1. 洛谷——P2656 采蘑菇

    P2656 采蘑菇 题目描述 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇.小胖和ZYR经过某条小径一次, ...

  2. 洛谷—— P2656 采蘑菇

    https://www.luogu.org/problem/show?pid=2656 题目描述 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连 ...

  3. [Luogu 2656] 采蘑菇

    Description 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇.小胖和ZYR经过某条小径一次,可以采 ...

  4. 洛谷 P2656 采蘑菇 树形DP+缩点+坑点

    题目链接 https://www.luogu.com.cn/problem/P2656 分析 这其实是个一眼题(bushi 发现如果没有那个恢复系数,缩个点就完了,有恢复系数呢?你发现这个恢复系数其实 ...

  5. 【Foreign】采蘑菇 [点分治]

    采蘑菇 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output Sample Input 5 1 2 3 2 3 1 2 1 ...

  6. 【细节题 离线 树状数组】luoguP4919 Marisa采蘑菇

    歧义差评:但是和题意理解一样了之后细节依然处理了很久,说明还是水平不够…… 题目描述 Marisa来到了森林之中,看到了一排nn个五颜六色的蘑菇,编号从1-n1−n,这些蘑菇的颜色分别为col[1], ...

  7. luogu P2056 采花

    题目描述 萧芸斓是 Z国的公主,平时的一大爱好是采花. 今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 1-c 表示) ,且花是排成 ...

  8. F 采蘑菇的克拉莉丝

    这是一道树链剖分的题目: 很容易想到,我们在树剖后,对于操作1,直接单点修改: 对于答案查询,我们直接的时候,我们假设查询的点是3,那么我们在查询的时候可分为两部分: 第一部分:查找出除3这颗子树以外 ...

  9. [Luogu1119]采蘑菇

    题目大意: 给你一个无向图,点i在时间t[i]之前是不存在的,有q组询问,问你时间为t时从x到y的最短路. 点的编号按出现的时间顺序给出,询问也按照时间顺序给出. 思路: Floyd. Floyd的本 ...

随机推荐

  1. router-view组件在app.vue中渲染不出来怎么办

    1.在app.vue使用router-view组件直接渲染 页面什么都没显示,可能问题出在路由配置上了,检查路由是否配置完好,路由挂载那里必须使用routes属性 2.在app.vue中router- ...

  2. 显示器类型对美乐威NDI IP转换器延时影响测试

    背景 用户在选择用网络传输视频时,传输延迟通常是他们非常关心的数据.集成商在探究如何降低视频传输延时,往往专注于网络本身和视频编码的优化,容易忽略视频解码和播出也是整个视频传输过程中非常重要的环节.本 ...

  3. Kubernetes 配置私有镜像仓库时,没有权限访问的问题

    使用 K8S 部署服务时,如果指定的镜像地址是内部镜像仓库,那么在下载镜像的时候可能会报权限错误.这是由于在 K8S 中部署服务时,K8S 需要到 Harbor 中进行一次验证,这个验证与节点中使用 ...

  4. 走在深夜的小码农 Sixth Day

    Css3 Six Day writer:late at night codepeasant 学习大纲: 一.其他样式 1.圆角边框 在 CSS3 中,新增了圆角边框样式,这样我们的盒子就可以变圆角了. ...

  5. 10 Servlet_02 资源跳转(主要是内部转发)与中文乱码问题

    总的知识点: 1.小的知识点总结: alt + shift + r 重命名快捷键(可以给包和类以及项目重命名) 有序列表 ol li 无序列表 ul type 格式 text 是文本类型 passwo ...

  6. EBAZ4205学习资源整理

    EBAZ4205是一块矿机的控制板,芯片是ZYNQ7010,某鱼上应该不超过30元就能买一块,垃圾佬狂喜 经过不复杂的操作就能进行正常开发,由于货量比较大现在已经有很多大佬写了很多很多好的资料,这里我 ...

  7. php 获取抖音id

    <?php public function getid($dy_url){ $header=get_headers($dy_url); $str = "/^.*?(\d+).*/&qu ...

  8. 03 . Vue基础之计算属性,组件基础定义和使用

    vue组件 fetch请求组件 fetch XMLHttpRequest是一个设计粗糙的API, 配置和调用方式非常混乱,而且基于事件的异步模型写起来不友好,兼容性不好. <!DOCTYPE h ...

  9. Spider_基础总结5--动态网页抓取--元素审查--json--字典

    # 静态网页在浏览器中展示的内容都在HTML的源码中,但主流网页使用 Javascript时,很多内容不出现在HTML的源代码中,此时仍然使用 # requests+beautifulsoup是不能够 ...

  10. BATJTMD,大厂招聘,都招什么样Java程序员?

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 Java学到什么程度可以找工作? 最近总看到类似这样的问题,也有一些工作3年左右的小 ...