#扩欧,同余方程#JZOJ 1158 洛谷 2421 荒岛野人

比赛


题目

N个野人,一开始依次住在山洞C1,C2,…,CN中,第i个野人会沿顺时针向前走Pi个洞住下来。每个野人i有一个寿命值L,至少要多少个山洞才能使没有任何两个野人在有生之年处在同一个山洞。


分析

枚举山洞的数量,计算出(山洞的数量为p)
C i + P i x C j + P j x ( m o d p )
移项后得到
P i x P j x C j C i ( m o d p )
C j C i 减去 p y 得到 ( P i P j ) x 可得
( P i P j ) x = C j C i p y
移项后
( P i P j ) x + p y = C j C i
扩欧找到x的答案
所以x的最小整数解是通过 ( P i P j ) x + p y = g c d ( P i P j , p ) 得到的
最后 x = x ( C j C i ) / g c d ( P i P j , p ) m o d p ( )
当x同时在两个野人的有生之年中,那么继续枚举,否则直接输出山洞的数量。


代码

#include <cstdio>
#include <cctype>
using namespace std;
int n,c[16],p[16],l[16],cave;
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int max(int a,int b){return (a>b)?a:b;}
int exgcd(int a,int b,int &x,int &y){
    if (b==0) {x=1;y=0; return a;}
    else{
        int d=exgcd(b,a%b,y,x);
        y-=a/b*x; return d;
    }
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) cave=max(cave,c[i]=in()),p[i]=in(),l[i]=in();
    while (1){
        bool flag=0; int d,x,y,a,b;
        for (int i=1;i<n;i++){
        for (int j=i+1;j<=n;j++){
            a=p[i]-p[j];b=c[j]-c[i];
            if (a<0) a=-a,b=-b;
            d=exgcd(a,cave,x,y); //扩欧
            int cav=cave/d;
            x=(x*b/d%cav+cav)%cav;
            if (!x) x+=cav;
            if (b%d==0&&x<=l[i]&&x<=l[j]) flag=1;//有生之年不会和平
            if (flag) break;
            }if (flag) break;
        }
        if (flag) cave++;
        else return !printf("%d",cave);
    }
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/80973040