1. 程式人生 > 實用技巧 >BZOJ-1407 [Noi2002]Savage(exgcd)

BZOJ-1407 [Noi2002]Savage(exgcd)

題目描述

  資料範圍:\(1\leq n\leq 15,1\leq C_i,P_i\leq 100,0\leq L_i\leq 10^6\),輸入資料保證有解,且 \(M\leq 10^6\)

分析

  給出 \(n\)\(C_i,P_i,L_i\),求最小的 \(M\) 使得對於任意的 \(i,j(1\leq i,j\leq n)\)

\[C_i+P_i\times x\equiv C_j+P_j\times x\pmod {M} \]

不成立。

  則:

\[C_i+P_i\times x+My=C_j+P_j\times x\\(P_i-P_j)x+My=C_j-C_i \]

  \(M\)

最大 \(10^6\),從小到大列舉 \(M\),用擴充套件歐幾里得演算法檢驗是否有解。若無解,\(M\) 即為所求;若有解,判斷 \(x_{\min}\leq L_i\)\(x_{\min}\leq L_j\) 是否成立,若成立說明有解,繼續列舉;若不成立,\(M\) 即為所求。

  時間複雜度 \(O(Mn^2\log C_i)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
int n;
long long C[1010],P[1010],L[1010];
long long exgcd(long long a,long long b,long long &x,long long &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    long long gcd=exgcd(b,a%b,y,x);
    y=y-x*(a/b);
    return gcd;
}
bool check(int M)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            long long a=P[i]-P[j];
            long long b=M;
            long long c=C[j]-C[i];
            long long x,y;
            long long gcd=exgcd(b,a%b,y,x);
            if(c%gcd!=0)
                continue;
            a=a/gcd;b=b/gcd;c=c/gcd;
            if(b<0)
                b=-b;
            x=(x*c%b+b)%b;
            if(x<=L[i]&&x<=L[j])
                return 0;
        }
    }
    return true;
}
int main()
{
    cin>>n;
    long long maxn=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d %d",&C[i],&P[i],&L[i]);
        maxn=max(maxn,C[i]);
    }
    for(int i=maxn;;i++)
    {
        if(check(i))
        {
            printf("%d\n",i);
            return 0;
        }
    }
    return 0;
}