其他分享
首页 > 其他分享> > AcWing 369. 北大ACM队的远足

AcWing 369. 北大ACM队的远足

作者:互联网

\(\text{Update on 2020.3.25}\)

我之前的做法也有问题,讨论还是不够严谨,导致又有几组(见 打卡评论区)\(\text{Hack}\) 数据,告诉了我们一条边可能覆盖两次,并且覆盖的不一定是连续的桥,解决方案详情见下面的评论区 / 真实的分类讨论,# 包括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

Input5:

1
2 0 0 1 1

Ans5:

-1

题目转化

如书中所述,在任何一条最短路中桥、每段桥之间的距离都是一定的。即若将一条路径看做一个区间,每个桥看做一条区间,桥区间的分布是一定的。题目转化为了用两条长为 \(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] $

真实的分类讨论

其他题解讨论的都不是很严谨。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100005, M = 200005, P = 1e9 + 7, INF = 0x3f3f3f3f;
int n, m, S, T, Q;
int head[N], bhead[N], pre[N], d[N], g[N], ds[N], dt[N], degS[N];
int a[N], tot, q[N], degT[N], disS[N], disT[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[N], int cnt[N], int dis[N], int deg[N]) {
    for (int i = 0; i < n; i++) cnt[i] = 0, dis[i] = INF;
	dis[s] = 0, cnt[s] = 1;
	int hh = 0, tt = 0; q[0] = s;
	while (hh <= tt) {
		int u = q[hh++];
		for (int i = h[u]; ~i; i = e[i].next) {
			int v = e[i].v;
			if (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;
    	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]++;
		}
		toposort(T, S, bhead, ft, disT, degT);
		if (disT[S] == INF) { puts("-1"); continue; }
		toposort(S, T, head, fs, disS, degS); 
		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] = disS[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;
}

标签:int,tot,桥边,ACM,bri,369,dt,ds,AcWing
来源: https://www.cnblogs.com/dmoransky/p/12567739.html