AcWing 369. 北大ACM队的远足
\(\text{Update on 2020.3.25}\)
我之前的做法也有问题,讨论还是不够严谨,导致又有几组(见 打卡评论区)\(\text{Hack}\)
此题数据极水,这里有几种错误写法:
Type 1
这 4 个数据,层层递进地告诉了我们一条边可能覆盖两次,并且覆盖的不一定是连续的桥,解决方案详情见下面的评论区 / 真实的分类讨论,# 包括Github上的标程好像也挂了(目前题解和打卡的代码貌似大多都是错的)。
Input1:
1
2 1 0 1 1
0 1 2
Ans1:
0
Input2:
1
2 1 0 1 2
0 1 3
Ans2:
0
Input3:
1
4 3 0 3 3
0 1 1
1 2 5
2 3 1
Ans3:
1
Input4:
1
5 5 0 4 3
0 1 1
1 2 1
1 2 1
2 3 3
3 4 1
Ans4:
0
Type 2
没有特判没有路径的无解情况,一开始我没写也过了。但是 OpenJudge 有卡这个。
Input5:
1
2 0 0 1 1
Ans5:
-1
Type 3
这是一种错误的拓扑排序求最短路方式,即只把 \(s\) 起点塞到队列里进行拓扑排序,若有一些零度点指向了该路径上的一些点,由于零度点并没有进入刷新,所以那些点的度不会被减到 \(0\),从而就炸裂了,被错判成无解。
奇妙的是,这么写可以 AC 并且过 OpenJudge。
正确的做法是按照往常一个做拓扑排序,只不过维护距离 \(d\) 数组的时候,初始化时将 \(d[s] = 0\),其余均赋值为正无穷,之后每条边该转移时更新一下即可。
Input6:
1
4 3 0 2 1
0 1 1
1 2 1
3 1 1
Ans6:
0
题目转化
如书中所述,在任何一条最短路中桥、每段桥之间的距离都是一定的。即若将一条路径看做一个区间,每个桥看做一条区间,桥区间的分布是一定的。题目转化为了用两条长为 \(Q\) 的线段覆盖尽可能多的区间,
如何检查是否为桥边
正反跑一次最短路,记录方案数,考虑一条边两边的方案数是否等于全局的即可。
书中未提到的 DP 方程
由于这题区间的值域很大 \(\le 10^9\),我们没办法开这么大的空间,但是可以贪心考虑。考虑找到一组最优解,将其向右平移,使他的右端点是一个桥边的右端点,这样答案不会变差(因为向右平移的过程都在右边答案贡献 \(+1\),左边贡献减 \(0\) 或 \(1\)),同理左端点也可以这么考虑。所以我们 \(DP\) 每次的决策只需考虑在每条桥边的端点开始 \(/\) 结束坐车即可。
一些定义
设 \(d[i]\) 为 从 \(S\) 到第 \(i\) 个节点的距离
设 \(g[i]\) 为 从 \(S\) 到第 \(i\) 个节点的桥的距离
设 \(bri[i]\) 设 \([i - 1, i]\) 这段是不是桥
这些玩意都是可以跑最短路后把最短路抽出来变成一条链 \(O(n + m)\) 处理的。
DP
设 \(ds[i]\) 为从 \(S\) 到第 \(i\) 个节点的最小危险程度
设 \(dt[i]\) 为从 \(i\) 到第 \(T\) 个节点的最小危险程度
处理 ds
如果 \(d[i] - d[i - 1]\) 这段是桥:
找到一个最小的 \(k\) 满足 \(d[i] - d[k] <= q\)。显然 \(i\) 增大的时候,\(k\) 也是不降的,具有单调性可以用双指针 \(O(n)\)。
如果 \(bri[k]\)
\(ds[i] = g[k] - (q - (d[i] - d[k]))\)
否则 \(ds[i] = g[k]\)
处理 dt
\(dt[i]\) 类似
找到一个最大的 \(k\) 使得 \(d[k] - d[i] <= q\)
如果 \(bri[k]\)
\(dt[i] = g[tot] - g[k] - (q - (d[k] - d[i]))\)
否则 $dt[i] = g[tot] - g[i] $
真实的分类讨论
其他题解讨论的都不是很严谨。
- 假设一条桥边上没有覆盖两次,那么必然可以用书中的放法,将桥边画一条界,两边分别取最优。
- 否则,假设最优解是一条桥边上覆盖两次的情况,那么存在一种方式是紧挨着的最优解,因为将他们平移至紧挨着答案不会变差,然后问题变成了 \(2 * Q\) 长度的一个覆盖,然后这个覆盖和之前类似,都是把线段平移到端点不会变差,这里就不再赘述。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005, M = 200005, P = 1e9 + 7, INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> PII;
int n, m, S, T, Q;
int head[N], bhead[N], pre[N], d[N], dis[N], g[N], ds[N], dt[N], degS[N];
int a[N], tot, q[N], degT[N], fs[N], ft[N], numE;
bool st[M << 2], bri[N];
struct E{
int next, v, w;
} e[M << 2];
void inline add(int u, int v, int w, int h[]) {
e[++numE] = (E) { h[u], v, w };
h[u] = numE;
}
void toposort(int s, int t, int h[], int cnt[], int deg[], int opt) {
for (int i = 0; i < n; i++) cnt[i] = 0;
cnt[s] = 1;
int hh = 0, tt = -1;
for (int i = 0; i < n; i++) if (!deg[i]) q[++tt] = i;
while (hh <= tt) {
int u = q[hh++];
for (int i = h[u]; ~i; i = e[i].next) {
int v = e[i].v;
if (opt && dis[u] + e[i].w < dis[v])
dis[v] = dis[u] + e[i].w, pre[v] = i;
(cnt[v] += cnt[u]) %= P;
if (--deg[v] == 0) q[++tt] = v;
}
}
}
void inline clear() {
numE = -1, tot = 0;
for (int i = 0; i < n; i++) {
head[i] = bhead[i] = -1, dis[i] = INF;
degS[i] = degT[i] = 0, bri[i] = false;
}
for (int i = 0; i < 2 * m; i++) st[i] = false;
}
int main() {
int Case; scanf("%d", &Case);
while (Case--) {
scanf("%d%d%d%d%d", &n, &m, &S, &T, &Q); clear();
for (int i = 1, u, v, w; i <= m; i++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w, head); add(v, u, w, bhead);
degS[v]++, degT[u]++;
}
dis[S] = 0;
toposort(S, T, head, fs, degS, 1);
if (dis[T] == INF) { puts("-1"); continue; }
toposort(T, S, bhead, ft, degT, 0);
a[++tot] = T;
while (a[tot] != S) a[tot + 1] = e[pre[a[tot]] ^ 1].v, tot++;
reverse(a + 1, a + 1 + tot);
for (int i = 1; i <= tot; i++) d[i] = dis[a[i]];
for (int u = 0; u < n; u++) {
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if ((LL)fs[u] * ft[v] % P == fs[T]) st[i] = true;
}
}
for (int i = 2; i <= tot; i++) bri[i] = st[pre[a[i]]], g[i] = g[i - 1] + (bri[i] ? e[pre[a[i]]].w : 0);
int k = 1;
for (int i = 2; i <= tot; i++) {
ds[i] = min(g[i], ds[i - 1] + g[i] - g[i - 1]);
while (k + 1 <= i && d[i] - d[k] > Q) k++;
ds[i] = min(ds[i], g[k] - (bri[k] ? Q - (d[i] - d[k]) : 0));
}
ds[1] = dt[tot] = 0, k = tot;
for (int i = tot - 1; i; i--) {
dt[i] = min(g[tot] - g[i], dt[i + 1] + g[i + 1] - g[i]);
while (k - 1 >= i && d[k] - d[i] > Q) k--;
dt[i] = min(dt[i], g[tot] - g[k] - (bri[k + 1] ? Q - (d[k] - d[i]): 0));
}
int ans = 2e9;
for (int i = 1; i <= tot; i++) ans = min(ans, ds[i] + dt[i]);
k = 1;
for (int i = 2; i <= tot; i++) {
while (k + 1 <= i && d[i] - d[k] > 2 * Q) k++;
ans = min(ans, g[tot] - g[i] + g[k] - (bri[k] ? 2 * Q - (d[i] - d[k]) : 0));
}
printf("%d\n", ans);
}
return 0;
}
AcWing 369. 北大ACM队的远足的更多相关文章
- 北大 ACM 分类 汇总
1.搜索 //回溯 2.DP(动态规划) 3.贪心 北大ACM题分类2009-01-27 1 4.图论 //Dijkstra.最小生成树.网络流 5.数论 //解模线性方程 6.计算几何 //凸壳.同 ...
- 北大ACM - POJ试题分类(转自EXP)
北大ACM - POJ试题分类 -- By EXP 2017-12-03 转载请注明出处: by EXP http://exp-blog.com/2018/06/28/pid-38/ 相关推荐文: 旧 ...
- 北大ACM题库习题分类与简介(转载)
在百度文库上找到的,不知是哪位大牛整理的,真的很不错! zz题 目分类 Posted by fishhead at 2007-01-13 12:44:58.0 -------------------- ...
- 北大ACM(POJ1014-Dividing)
Question:http://poj.org/problem?id=1014 问题点:抽屉原理.dfs.多重背包. Memory: 248K Time: 16MS Language: C++ Res ...
- 北大ACM(POJ1013-Counterfeit Dollar)
Question:http://poj.org/problem?id=1013 问题点:排除+验证. Memory: 244K Time: 16MS Language: C++ Result: Acc ...
- 北大ACM(POJ1012-Joseph)
Question:http://poj.org/problem?id=1012 问题点:约瑟夫环. Memory: 220K Time: 329MS Language: C++ Result: Acc ...
- 北大ACM(POJ1010-STAMPS)
Question:http://poj.org/problem?id=1010问题点:DFS.剪枝. Memory: 220K Time: 32MS Language: C++ Result: Acc ...
- 北大ACM(POJ1009-Edge Detection)
Question:http://poj.org/problem?id=1009问题点:RLE编码. Memory: 648K Time: 547MS Language: C++ Result: Acc ...
- 北大ACM(POJ1008-Maya Calendar)
Question:http://poj.org/problem?id=1008 问题点:日历转换. Memory: 280K Time: 16MS Language: C++ Result: Acce ...
随机推荐
- binary hacks读数笔记(ld 链接讲解 一)
首先我们先看两段代码: a.c extern int shared; int main(){ int a=100; swap(&a,&shared); } b.c int shared ...
- logback怎么写?分类输出日志到不同的文件
此appender有顺序,最好不要乱调顺序,输出日志如下: drwxr-xr-x 2 root root 4096 Dec 3 00:00 2019-12-02drwxr-xr-x 2 root ro ...
- C语言复习系列-转义字符
C语言复习系列-转义字符 准备学习stm32单片机,感觉需要复习一下C语言,翻看了菜鸟教程,竟然有不少地方看不懂,真让人汗颜······ 转义字符大部分语言里面都有,至少我学过的里面都有,在这些语言中 ...
- 用JavaScript实现全选-反选
实现全选-反选 在日常生活我们会遇到需要全选-反选的地方,其实用JavaScript也能实现. 样式如下所示: 样式代码如下所示: <!DOCTYPE html PUBLIC "-// ...
- 背包问题(动态规划 C/C++)
Description 卖方:这件商品14元 买方:给你20元 卖方:不好意思,我的零钱不够 买方:好吧,这是15元,剩的当小费 当到一个地方旅游时,如果你买东西的地方不支持信用,带零钱还是非常有用的 ...
- Ramnit蠕虫病毒分析和查杀
Ramnit是一种蠕虫病毒.拥有多种传播方式,不仅可以通过网页进行传播,还可以通过感染计算机内可执行文件进行传播.该病毒在2010年第一次被安全研究者发现,从网络威胁监控中可以看出目前仍然有大量的主机 ...
- kali 系列学习09-Kali-linux设置ProxyChains
ProxyChains是Linux和其他Unices下的代理工具.它可以使任何程序通过代理上网,允许TCP和DNS通过代理隧道,支持HTTP.SOCKS4和SOCKS5类型的代理服务器,并且可配置多个 ...
- 关于Redis的一些思考
1.从Java语言考虑,已经有ConcurrentHashMap等并发集合类了,与Redis相比,区别于差异在哪? 一直有这么个疑问,今天有搜了很久,很巧,搜到个有同样想法的问答,如下: When p ...
- 关于Java集合框架,这篇讲的还算不错了,建议大家看看!
集合框架 为什么要用集合而非数组: 虽然数组是保存一组对象最有效的方式,但是数组具有固定尺寸,但在写程序时并不知道将需要多少个对象.而集合能够自动地调整自己的尺寸. 打印数组时,必须使用Arrays. ...
- ABBYY FineReader 15 如何为PDF文档添加页眉页脚
页眉.页脚是文档页面顶部或底部重复出现的文本信息.很多用户会习惯在文档页面的顶部与底部区域添加页眉.页脚来展现页码.文档标题.作者姓名.品牌名称等附加信息.而ABBYY FineReader 15(W ...