NOI2002 荒岛野人
可以发现转圈的过程类似于对一个数进行取模,所以我们不妨将所有的点下标设为以\(0\)开始,即对于输入的\(c_{i}\),我们将其减\(1\)。
还可以发现答案不超过\(10^6\),意味着我们可以枚举答案,然后检查答案是否合法。
容易发现\(ans\)不合法,当且仅当存在\(1\leq i,j\leq n\),满足:
\[c_{i}+kp_{i}\equiv c_{j}+kp_{j} \mod ans
\]
注意到这里只有一个未知数\(k\),很像同余方程中\(ax\equiv 1\mod p\)的形式。
所以接下来就是喜闻乐见的推式子环节:
\[c_{i}+kp_{i}\equiv c_{j}+kp_{j} \mod ans
\]
\[c_{i}-c_{j}+k(p_{i}-p_{j})\equiv 0 \mod ans
\]
\[k(p_{i}-p_{j})\equiv c_{j}-c_{i} \mod ans
\]
这时就和同余方程中的解法一样,设另一个未知数\(l\),满足:
\[k\cdot (p_{i}-p_{j})+l\cdot ans=c_{j}-c_{i}
\]
当然这里\(l\)可以是负数,但必须为整数。
那么我们惊喜的发现,这个方程长得像\(ax+by=c\)的形式,而这显然可以使用exgcd求出一对合法的\(x,y\),然后找到\(k\geq 0\)的最小的整数解。(\(l\)可以为负数,但\(k\)必须是自然数)
如果在枚举\(i,j\)时遇到不合法的可以直接break,这样能使程序快不止一点。
代码:
#include <bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
using ll = long long;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
ll ans = exgcd(b, a % b, x, y), tmp = x;
x = y, y = tmp - a / b * y;
return ans;
}
int n;
ll c[20], p[20], l[20];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &c[i], &p[i], &l[i]);
c[i]--;
}
for (ll ans = *std::max_element(c + 1, c + n + 1) + 1;; ans++) {
int can = 1;
for (int i = 1; i <= n && can; i++) {
for (int j = 1; j <= n && can; j++) {
if (i == j || p[i] > p[j])
continue;
ll x = 0, y = 0, rec = exgcd(-(p[i] - p[j]), ans, x, y);
x = x * (c[i] - c[j]) / rec, y = y * (c[i] - c[j]) / rec;
if ((c[j] - c[i]) % rec != 0)
continue;
ll M = ans / rec;
x = (x % M + M) % M;
if (x <= l[i] && x <= l[j])
can = 0;
}
}
if (can) {
printf("%lld\n", ans);
exit(0);
}
}
return 0;
}
浙公网安备 33010602011771号