[AGC023D] Go Home

wzxx

2021-03-24 14:50:15

Solution

[题目链接](https://www.luogu.com.cn/problem/AT3955) 题目中,每一个人的投票策略都是投 “使自己尽快到家” 的方向。 第一眼看上去,以为等价于 “投自己家的方向” ,其实并不然。 观察样例,发现第一个样例就很好地解释了他们的投票策略。 输入: ```cpp 3 2 1 3 3 4 4 2 ``` 输出: ```cpp 4 ``` 显然,车行驶的路线是 2 → 1 → 2 → 3 → 4 ,先左再右。 但是一开始时,住在右边的人明明比住在左边的人多 3 个,为什么不往右开呢? 假设先往右开,在位置 3 有 4 个人下了车,此时支持往右开的只剩 2 个人,被住在左边的 3 个人碾压,势单力薄的他们根本无法使车继续往右开,只能让车先开到 1 再开回 4 ,这还不如一开始就往左开。 所以,在开始的时候,住在 4 的 2 个人投了左方向,使得车先往左开。 可以发现,人们之所以不一定会投自己家的方向,是因为在驶向自己家的途中,有一批人会下车,使得车到不了自己家又开回去了,得不偿失,所以这一部分人会选择投反方向。 我们考虑最左端的房子 $X_l$ 和最右端的房子 $X_r$ 。 如果 $S < X_l$ 或 $S > X_r$ ,可以直接计算。 考虑 $X_l < S < X_r$ 。不难发现,如果 $P_l \geq P_r$,车子一定会先到达 $X_l$ 再到达 $X_r$ 。 假设车子先往 $X_r$ 的方向走, 因为 $P_l \geq P_r$ ,所以还没等走到 $X_r$ ,右边的票数就会减少到小于左边的票数,车子就会调头, 住在 $X_r$ 的人就会反水投左边。所以不管怎么样, $X_r$ 的人都会投左边,所以车子必然先到 $X_l$ 再到 $X_r$ 。 既然这样,我们干脆让 $X_r$ 的人都搬到 $X_l$ ,累积上 $X_r - X_l$ 的代价后递归处理。当 $P_l < P_r$ 时同理。边界条件为 $S < X_l$ 或 $S > X_r$ 。 于是,我们就得到了一个时间复杂度 $O(n)$ 的算法。 代码有点小细节。 ```cpp #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 100005; int n = 0, S = 0, x[N]; long long p[N]; long long rd() { long long x = 0; char ch = getchar(); while (ch < '0' || ch > '9') ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); return x; } long long Sol(int l, int r, int opt) { if (S < x[l]) return x[r] - S; if (S > x[r]) return S - x[l]; if (p[l] >= p[r]) { p[l] += p[r]; return Sol(l, r - 1, 1) + (opt ? 0 : (x[r] - x[l])); } p[r] += p[l]; return Sol(l + 1, r, 0) + (opt ? (x[r] - x[l]) : 0); } int main() { n = rd(), S = rd(); for (int i = 1; i <= n; i++) x[i] = rd(), p[i] = rd(); printf("%lld", Sol(1, n, (p[1] >= p[n]) ? 0 : 1)); return 0; } ```