【bzoj1407】 Noi2002—Savage

http://www.lydsy.com/JudgeOnline/problem.php?id=1407 (题目链接)

题意

  有$n$个原始人他们一开始分别住在第$c[i]$个山洞中,每过一年他们都会迁往第$(c[i]+p[i])%m$个山洞,每个原始人的寿命分别为$l[i]$,求他们在生命终结前使没有两个人同住一个山洞中时最少需要有多少个山洞。

Solution

  我们可以枚举答案$m$。

  根据条件设经过$x$年后两个原始人$i$,$j$相撞。$$c[i]+p[i]*x=c[j]+p[j]*x~(mod~m)$$

$$(p[i]-p[j])*x=c[j]-c[i]~(mod~m)$$

$$(p[i]-p[j])*x+m*y=c[j]-c[i]$$

  这样就可以exgcd做了。

  若$gcd(p[i]-p[j],m)$不是$c[j]-c[i]$的约数,那么他们永远不可能相遇。

  若求出来的最小正数$x$小于他们两个的寿命,那么当前的$m$就不合法。

代码

// bzoj1407
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

int n,c[20],p[20],l[20];

void exgcd(int a,int b,int &d,int &x,int &y) {
    if (b==0) {d=a;x=1;y=0;return;}
    exgcd(b,a%b,d,y,x);
    y-=(a/b)*x;
}
bool check(int m) {
    int d,x,y;
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++) {
            int C=((c[j]-c[i])%m+m)%m;
            int P=((p[i]-p[j])%m+m)%m;
            exgcd(P,m,d,x,y);
            if (C%d!=0) continue;
            x=((C/d)*x%(m/d)+(m/d))%(m/d);
            if (x<=l[i] && x<=l[j]) return 0;
        }
    return 1;
}
int main() {
    scanf("%d",&n);
    int ans=0;
    for (int i=1;i<=n;i++) {
        scanf("%d%d%d",&c[i],&p[i],&l[i]);
        ans=max(ans,c[i]);c[i]--;
    }
    for (;ans<=1000000;ans++) if (check(ans)) break;
    printf("%d",ans);
    return 0;
}

  

This passage is made by MashiroSky.
原文地址:https://www.cnblogs.com/MashiroSky/p/5912814.html