Luogu P2656 采蘑菇
尽管是缩点的习题,思路也是在看了题解后才明白的。
首先,每个强连通分量内的点都是一定互通的,也就是可以完全把这里面的边都跑满,摘掉所有能摘的蘑菇。那么,考虑给每一个强连通分量化为的新点一个点权,代表摘光蘑菇能拿到的边权之和。然后,在新点之间保留原来的桥及其初始权值。(每一个桥一定只能跑一遍,否则说明这两个本应单向通行的分量之间有返回的路径,则二者可构成一个更大的分量。这个结论正是tarjan算法求有向图dcc的核心原理。)现在得到了一张新图,问题在于如何在一张包含点权、边权的DAG上求起始于定点的最长路。
这个问题可以用拓扑序DP求解。在dp求最长路的基础上,为了保证一定由s点所在分量起始,我们把该分量初状态设为其权值,其余点都赋初值为INT_MIN。
这样dp得到的最长路一定是基于f[dcc[s]]求出的。
另外,用SPFA算法来跑点权、边权交叉的最长路是可行的,不过应用于本题复杂度不如dp优秀。-----------------------------------------
在参阅题解后,基于一开始跑偏的假设,笔者又想到了一个貌似更优的解法。
实际上我们并不需要考虑原图的所有节点。容易想到,从给定起点向外作一轮tarjan算法(dfs)不能达到的点,在新图中也不可能走到。因此,我们只需要对图中以s为原点作一次tarjan能够跑到的几个连通分量进行缩点,这样能够到达的区域就变成了一棵以s为根的树(8月20日订正:这里的“树”更严谨的说法是“树形图”)。我们只需要再作一次dfs求出最深叶节点的深度即可。
(注:以下代码注释分部分为除最后一种思路的其余解法,仅供参考)
- #include <cstdio>
- #include <iostream>
- #include <queue>
- #include <climits>
- #define rint register int
- #define BUG putchar('*')
- #define maxn 80010
- #define maxm 200010
- using namespace std;
- struct E {
- int to, nxt, w;
- double op;
- } edge[maxm], edge2[maxm];
- int n, m, st;
- int head[maxn], top;
- inline void insert(int u, int v, int w, double op) {
- edge[++top] = (E) {v, head[u], w, op};
- head[u] = top;
- }
- int dfn[maxn], low[maxn], sta[maxn], stp, timer;
- bool ins[maxn], vis[maxn];
- int cnt, c[maxn];
- void dfs(int u) {
- dfn[u] = low[u] = ++timer;
- sta[++stp] = u;
- ins[u] = true;
- vis[u] = true;// 仅搜一次标记所答点
- for (rint i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (!dfn[v]) {
- dfs(v);
- low[u] = min(low[u], low[v]);
- } else if (ins[v])
- low[u] = min(low[u], dfn[v]);
- }
- if (dfn[u] == low[u]) {
- ++cnt;
- int x;
- do {
- x = sta[stp--];
- ins[x] = false;
- c[x] = cnt;
- } while (x != u);
- }
- }
- void tarjan() {
- // for (int i = 1; i <= n; ++i) // 全图tarjan
- // if (!dfn[i]) dfs(i);
- dfs(st);
- }
- int head2[maxn], top2;
- inline void insert2(int u, int v, int w) {
- edge2[++top2] = (E) {v, head2[u], w, 0};
- head2[u] = top2;
- }
- int val[maxn], ind[maxn];
- void build() {
- rint v, w;
- for (rint u = 1; u <= n; ++u)
- if (vis[u])// 仅考虑一次搜索 缩点得树
- for (int i = head[u]; i; i = edge[i].nxt) {
- v = edge[i].to;
- w = edge[i].w;
- if (c[u] == c[v]) {
- register double op = edge[i].op;
- while (w)
- val[c[u]] += w, w *= op;
- } else
- insert2(c[u], c[v], w), ind[c[v]]++;
- }
- }
- //************************
- /* DAG 拓扑序dp
- int f[maxn];
- queue<int> q;
- int dp() {
- int ans = val[c[st]];
- for (int i = 1; i <= cnt; ++i) {
- f[i] = INT_MIN;
- if (!ind[i]) q.push(i);
- }
- f[c[st]] = val[c[st]];
- while (!q.empty()) {
- int u = q.front(); q.pop();
- for (int i = head2[u]; i; i = edge2[i].nxt) {
- int v = edge2[i].to;
- f[v] = max(f[v], f[u] + edge2[i].w + val[v]);
- --ind[v];
- if (!ind[v])
- ans = max(ans, f[v]), q.push(v);
- }
- }
- return ans;
- }
- */
- //**************************
- /* spfa
- bool inq[maxn];
- int dist[maxn];
- int spfa() {
- for (int i = 1; i <= cnt; ++i)
- dist[i] = INT_MIN;
- dist[c[st]] = val[c[st]];
- queue<int> q;
- inq[c[st]] = true, q.push(c[st]);
- while (!q.empty()) {
- int u = q.front();
- q.pop(), inq[u] = false;
- for (int i = head2[u]; i; i = edge2[i].nxt) {
- int v = edge2[i].to;
- if (dist[v] < dist[u] + edge2[i].w + val[v]) {
- dist[v] = dist[u] + edge2[i].w + val[v];
- if (!inq[v])
- q.push(v), inq[v] = true;
- }
- }
- }
- int ans = 0;
- for (int i = 1; i <= cnt; ++i)
- ans = max(ans, dist[i]);
- return ans;
- }*/
- //***************************
- int ans;
- void dfs2(int u, int dist) {
- dist += val[u];
- if (!head2[u]) {
- ans = max(ans, dist);
- return;
- }
- for (int i = head2[u]; i; i = edge2[i].nxt)
- dfs2(edge2[i].to, dist + edge2[i].w);
- }
- int main() {
- scanf("%d %d", &n, &m);
- int u, v, w;
- double op;
- for (rint i = 1; i <= m; ++i) {
- scanf("%d %d %d %lf", &u, &v, &w, &op);
- insert(u, v, w, op);
- }
- scanf("%d", &st);
- tarjan();
- build();
- // printf("%d", spfa());
- // printf("%d", dp());
- dfs2(c[st], 0);
- printf("%d", ans);
- return 0;
- }
这个题最大的收获是发现有向图缩点总跟DAG上的topo+DP有联系。按拓扑序遍历到某一点u,意味u点所有的入点都已经对其完成了更新,此时u点的状态满足无后效性。以及在DAG上求解始于某点的最长路径时,对f数组的特殊处理。
Luogu P2656 采蘑菇的更多相关文章
- 洛谷——P2656 采蘑菇
P2656 采蘑菇 题目描述 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇.小胖和ZYR经过某条小径一次, ...
- 洛谷—— P2656 采蘑菇
https://www.luogu.org/problem/show?pid=2656 题目描述 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连 ...
- [Luogu 2656] 采蘑菇
Description 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇.小胖和ZYR经过某条小径一次,可以采 ...
- 洛谷 P2656 采蘑菇 树形DP+缩点+坑点
题目链接 https://www.luogu.com.cn/problem/P2656 分析 这其实是个一眼题(bushi 发现如果没有那个恢复系数,缩个点就完了,有恢复系数呢?你发现这个恢复系数其实 ...
- 【Foreign】采蘑菇 [点分治]
采蘑菇 Time Limit: 20 Sec Memory Limit: 256 MB Description Input Output Sample Input 5 1 2 3 2 3 1 2 1 ...
- 【细节题 离线 树状数组】luoguP4919 Marisa采蘑菇
歧义差评:但是和题意理解一样了之后细节依然处理了很久,说明还是水平不够…… 题目描述 Marisa来到了森林之中,看到了一排nn个五颜六色的蘑菇,编号从1-n1−n,这些蘑菇的颜色分别为col[1], ...
- luogu P2056 采花
题目描述 萧芸斓是 Z国的公主,平时的一大爱好是采花. 今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 1-c 表示) ,且花是排成 ...
- F 采蘑菇的克拉莉丝
这是一道树链剖分的题目: 很容易想到,我们在树剖后,对于操作1,直接单点修改: 对于答案查询,我们直接的时候,我们假设查询的点是3,那么我们在查询的时候可分为两部分: 第一部分:查找出除3这颗子树以外 ...
- [Luogu1119]采蘑菇
题目大意: 给你一个无向图,点i在时间t[i]之前是不存在的,有q组询问,问你时间为t时从x到y的最短路. 点的编号按出现的时间顺序给出,询问也按照时间顺序给出. 思路: Floyd. Floyd的本 ...
随机推荐
- 从APT攻击中学习
0x01. 什么是APT? 可以看出APT攻击,叫高级可持续威胁攻击,也称为定向威胁攻击:什么是定向,也就是指定目标行业而发起进攻 这边又提到供应链和社会工程学,那是什么? 社会工程学,也就是社工,通 ...
- 嵌入式linux和stm32嵌入式开发这两者之间有什么关联性
对于更开始入坑的同学,可能也像我一样搞不清楚两者的区别与联系.现在结合知乎网上的相关资料发一篇文章来具体分析. 基于STM32的开发属于微控制器开发领域,主要开发工具是keil或IAR,这种开发更准确 ...
- symfony3.4 配置高效的邮箱swiftmailer插件
网站经常需要邮件发送,symfony里面swiftmailerBundle是一个非常并且稳定的发送邮件的第三方插件. 我们来看一下symfony里面的邮件怎么配置. 第一步我们先下载bundle: c ...
- 记一次微信公众号(微信H5)网页对接腾讯云慧眼人脸核验开发笔记
需求是这样的,客户需要一个在观看学习视频前弹出人脸识别核验真人的功能,客户找了个APP作为参考,但是在微信上第三方人脸识别是无法直接调取到前置摄像头的,都是通过用户自己获取用户的身份信息或者照片,然后 ...
- 团灭 LeetCode 打家劫舍问题
有读者私下问我 LeetCode 「打家劫舍」系列问题(英文版叫 House Robber)怎么做,我发现这一系列题目的点赞非常之高,是比较有代表性和技巧性的动态规划题目,今天就来聊聊这道题目. 打家 ...
- 第 2 篇:上手 Vue 展示 todo 列表
作者:HelloGitHub-追梦人物 追梦人物的 Vue 系列教程在他的博客已经全部更新完成,地址: https://www.zmrenwu.com/courses/vue2x-todo-tutor ...
- C3P0和Druid数据库连接池
目录 C3P0连接池 步骤: C3P0初始化: 创建C3P0工具类: 创建C3P0测试类: Druid连接池(由阿里巴巴提供的数据库连接池实现技术) 步骤: Druid初始化: 创建Druid工具类: ...
- UNION 和 UNION ALL的区别,一个例子就看明白
[UNION ALL] select a,b,sum(sm) AS s1, SUM(qm) AS s2 from ( select 'a' AS a, 'b' AS b, 2 AS sm, 200 A ...
- [MIT6.006] 19. Daynamic Programming I: Fibonacci, Shortest Path 动态规划I:斐波那契,最短路径
这节课讲动态规划的内容,动态规划是一种通用且有效的算法设计思路,它的主要成分是"子问题"+"重用".它可以用于斐波那契和最短路径等问题的求解上. 一.斐波那契 ...
- Centos7上一次War包的部署与运行
Centos7上一次War包的部署与运行 前言 由于前段时间第一次部署一个小型的项目,时间一长所以有些步骤有时候时间一长就忘了,在此做个简单的记录 一.原始系统开发环境 操作系统:Windows10: ...