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;
}
posted @ 2022-06-29 13:37  Nastia  阅读(29)  评论(0)    收藏  举报