Skip to content

洛谷 - P2421 荒岛野人

检测到 KaTeX 加载失败,可能会导致文中的数学公式无法正常渲染。

#题面

#题目描述

克里特岛以野人群居而著称。岛上有排列成环行的 mm 个山洞。这些山洞顺时针编号为 1,2,,m1, 2, \dots, m。岛上住着 nn 个野人,一开始依次住在山洞 C1,C2,,CnC_1, C_2, \dots, C_n中,以后每年,第 ii 个野人会沿顺时针向前走 PiP_i 个洞住下来。

每个野人 ii 有一个寿命值 LiL_i,即生存的年数。

下面四幅图描述了一个有 66 个山洞,住有三个野人的岛上前四年的情况。三个野人初始的洞穴编号依次为 1,2,31, 2, 3;每年要走过的洞穴数依次为 3,7,23, 7, 2;寿命值依次为 4,3,14, 3, 1

奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?

#输入格式

11 行为一个整数 nn,即野人的数目。

22 行到第 n+1n + 1 每行为三个整数 Ci,Pi,LiC_i, P_i, L_i,表示每个野人所住的初始洞穴编号,每年走过的洞穴数及寿命值。

#输出格式

仅包含一个数 mm,即最少可能的山洞数。输入数据保证有解,且 mm 不大于 10610^6

#输入输出样例

样例输入 #1

3
1 3 4
2 7 3
3 2 1

样例输出 #1

6

#数据范围与约定

对于 100%100\% 的数据,1n151 \leq n \leq 151Ci,Pi1001\leq C_i, P_i \leq 1000Li1060\leq L_i\leq 10^6,最终答案 m106m \leq 10^6

#思路

iji \ne ji,j[1,n]i, j \in [1, n] 时,第 ii 个野人和第 jj 个野人在同一个山洞中当且仅当情况如下:

Ci+xPiCj+xPj(modm)(1)\tag{1} C_i + x P_i \equiv C_j + x P_j \pmod m

那么只需要使其在 [0,min(Li,Lj)][0, \min(L_i, L_j)] 范围内无解即可维持岛上的和平。

移项,有:

x(PiPj)CjCi(modm)(2)\tag{2} x (P_i - P_j) \equiv C_j - C_i \pmod m

那么可以使用扩展欧几里得算法求出 (2)(2) 式的解:

(PiPj)x+my=CjCi(3)\tag{3} (P_i - P_j) x + my = C_j - C_i

再观察数据范围,发现答案 m106m \leq 10^6,那么可以考虑在 [max1inCi,106][\max_{1 \leq i \leq n} C_i, 10^6] 区间内枚举答案,复杂度为 O(mn2logCi)O(m n^2 \log C_i),可以通过本题。

题外话:今天下午打模拟赛的时候这道题的 (3)(3) 式推错了没切掉,真是可惜啊。

注:本题答案不具有单调性,不能二分。

#代码

C++
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <algorithm>
#include <iostream>
#include <tuple>

using std::cin;
using std::cout;
const char endl = '\n';

const int N = 20;

int n, max, ans;
std::tuple<int, int, int> a[N];

int exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
return a;
}

int g = exgcd(b, a % b, y, x);
y -= a / b * x;
return g;
}

bool check(int k) {
for (int i = 1; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
int c1, p1, l1, c2, p2, l2;
std::tie(c1, p1, l1) = a[i];
std::tie(c2, p2, l2) = a[j];

int a = p1 - p2,
b = k,
c = c2 - c1;

int x, y,
g = exgcd(a, b, x, y);

if (c % g) continue;

// 此处 g 可能是负数,因此需要取绝对值
a /= g, b = std::abs(b / g), c /= g;
x = (x * c % b + b) % b;

if (x <= std::min(l1, l2)) return false;
}
}

return true;
}

int main() {
std::ios::sync_with_stdio(false);

cin >> n;

for (int i = 1, c, p, l; i <= n; i++) {
cin >> std::get<0>(a[i]) >> std::get<1>(a[i]) >> std::get<2>(a[i]);

max = std::max(max, std::get<0>(a[i]));
}

for (int i = max; i <= 1000000; i++) {
if (check(i)) {
cout << i << endl;

exit(0);
}
}

return 0;
}

感谢 JohnSonloy 指出本文中的错误,已修正。