[AGC062D] Walk Around Neighborhood 题解

Description

给定正整数 NNNN 个正偶数 DiD_i

在平面直角坐标系中,小麦初始在 (0,0)(0,0),每次他可以选取一个未被擦去的 DiD_i,将其擦去,并从 (x,y)(x,y) 移动到 (x,y)(x',y') 使得 xx+yy=Di|x-x'|+|y-y'|=D_i。注意,所有坐标都是实数而非整数。例如,Di=2D_i=2,你可以从 (0,0)(0,0)(0.85486,1.14514)(0.85486,1.14514)

请问经过 NN 次移动后,是否可以回到 (0,0)(0,0)

如果可以的话,对于所有到达的点 (x,y)(x,y) 请求出 max(x+y)\max(|x|+|y|) 可能取到的最小值是多少。

1N2×105,2Di2×105,2Di1\leq N\leq 2\times 10^5,2\leq D_i\leq 2\times 10^5,2|D_i

Solution

先对 DiD_i 进行排序。显然无解的条件是 Dn>i=1n1DiD_n>\sum_{i=1}^{n-1}{D_i}

不妨设最小答案为 rr,那么每次跳都不能跳出半径为 rr 的圆。有个结论是 Dn2rDn\frac{D_n}{2}\leq r\leq D_n,这是因为当 r=Dnr=D_n 时,由于 i=1n1DiDn\sum_{i=1}^{n-1}{D_i}\leq D_n,所以只需要从小到大往一个方向走,如果走出这个圆了则最后一步一定能调整方向并走到圆上,于是走到第 n1n-1 步时一定在圆的边界上,最后一步走 DnD_n 的距离刚好回到原点。

对于 r<Dn2r<\frac{D_n}{2} 的情况,则无论在圆内的哪个点走 DnD_n 的距离一定会走出圆,就矛盾了。


然后判断 rr 是否是最小值。

由于 rr 是最小值,所以一定会存在某个时刻走到圆的边界上,并且因为要回来,所以问题等价于将 DD 划分成两个子序列,两个子序列分别可以走到边界。

考虑一个 DD 数组能走到边界 rr 的条件,如果 DiD_i 中小于等于 rr 的数的和 r\geq r,则这些数一定能走到 rr,大于 rr 的进行调整即可。如果和小于 rr,不妨设小于等于 rr 的数走到了 pp,下一步走 xx,如果 x>p+rx>p+r 就说明这时走 xx 会走出圆,否则一定能刚好走到圆边界,于是最优条件一定是选大于 rr 的最小的 xx 走,如果这么走都会出去则走不到,否则走得到。

求答案时先从小到大枚举 rr,并将所有 r\leq r 的数加到背包里,分类讨论一下两个子序列是哪种合法的情况即可。具体见代码。

时间复杂度:O(nVω)O\left(\frac{nV}{\omega}\right)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 2e5 + 5;

int n;
int a[kMaxN];
std::bitset<kMaxN> f;

bool check(int l, int r) {
int p = f._Find_next(l - 1);
return p <= r;
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
std::sort(a + 1, a + 1 + n);
int ss = 0;
for (int i = 1; i < n; ++i) ss += a[i];
if (ss < a[n]) return void(std::cout << "-1\n");
f[0] = 1;
int sum = 0;
a[n + 1] = 1e9;
for (int r = a[n] / 2, p = 1; r <= a[n]; ++r) {
for (; a[p] <= r; f |= (f << a[p++])) sum += a[p];
if (check(r, sum - r)) return void(std::cout << r << '\n');
if (p <= n && check(r, sum + r - a[p])) return void(std::cout << r << '\n');
if (p <= n - 1 && check(a[p] - r, sum + r - a[p + 1])) return void(std::cout << r << '\n');
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

[AGC062D] Walk Around Neighborhood 题解
https://sobaliuziao.github.io/2024/10/10/post/b27839ff.html
作者
Egg_laying_master
发布于
2024年10月10日
许可协议