NOI2002 荒岛野人

题目

Luogu P2421

克里特岛以野人群居而著称。岛上有排列成环行的 \(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\)

img

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

输出仅包含一个数 \(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;
}
posted @ 2022-01-02 23:12  f(k(t))  阅读(58)  评论(0编辑  收藏  举报