NOI2002 荒岛野人
题目
克里特岛以野人群居而著称。岛上有排列成环行的 \(m\) 个山洞。这些山洞顺时针编号为 \(1,2,\dots ,m\) 。岛上住着 \(n\) 个野人,一开始依次住在山洞 \(C_1,C_2,\dots ,C_n\) 中,以后每年,第 \(i\) 个野人会沿顺时针向前走 \(P_i\) 个洞住下来。
每个野人 \(i\) 有一个寿命值 \(L_i\),即生存的年数。
下面四幅图描述了一个有 \(6\) 个山洞,住有三个野人的岛上前四年的情况。三个野人初始的洞穴编号依次为 \(1,2,3\);每年要走过的洞穴数依次为 \(3,7,2\);寿命值依次为 \(4,3,1\)。
奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?
输出仅包含一个数 \(M\) ,即最少可能的山洞数。输入数据保证有解,且 \(M\) 不大于 \(10^6\)
分析
若在第 \(k\) 年,野人 \(i\) 和野人 \(j\) 所处的山洞重合,那么有:
\[C_i+kP_i \equiv C_j+kP_j \pmod{M}
\]
移项得:
\[k(P_i-P_j) \equiv C_j-C_i \pmod{M}
\]
若 \(M\) 已知,那我们可以用EXGCD求出上面的同余方程的一个解,进而求出最小正整数解,若最小正整数解 \(k \leq \min(L_i,L_j)\) 则会发生山洞重合的情况,野人会发生冲突,否则不会
已知 \(M\) 的范围并不大,所以可以从小到大枚举
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
int c[20], p[20], l[20];
ll gcd(ll a, ll b)
{
return a % b == 0 ? b : gcd(b, a % b);
}
ll exgcd(ll a, ll b, ll & x, ll & y)
{
if(b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
bool check(int m)
{
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
int a = ((p[j] - p[i]) % m + m) % m;
int b = ((c[i] - c[j]) % m + m) % m;
ll d = gcd(a, m);
if(b % d != 0)
continue;
ll x, y, t;
exgcd(a, m, x, y);
t = m / d;
x = (x * b / d % t + t) % t;
if(x <= min(l[i], l[j]))
return false;
}
}
return true;
}
int main()
{
cin >> n;
int minn = n;
for(int i = 1; i <= n; i++) {
cin >> c[i] >> p[i] >> l[i];
minn = max(minn, c[i]);
c[i]--;
}
for(int m = minn; m <= 1000000; m++) {
if(check(m)) {
cout << m << endl;
break;
}
}
return 0;
}