首页 > 技术文章 > Comet OJ

guangheli 2019-09-07 16:22 原文

考试的时候推出来了,但是忘了 $exgcd$ 咋求,成功爆蛋~

这里给出一个求最小正整数解的模板:

ll solve(ll A,ll B,ll C) 
{    
    ll x,y,g,b,ans; 
    gcd = exgcd(A,B,x,y);                 
    if(C%gcd!=0) return -1;        
    x*=C/gcd,B/=gcd; 
    if(B<0) B=-B;   
    ans=x%B;      
    if(ans<=0) ans+=B;  
    return ans;        
} 

大概就是这样. 

说一下题:

可以将题目转化成求 $\frac{ans(ans+1)}{2}\mod n=0$ 的最小 $ans$.
将式子转化一下,即 $ans(ans+1)=2n\times y$,其中 $y$ 是个整数.
易得 $ans$ 与 $ans+1$ 是互质的,所以 $2n$ 中每一种不同的质因子只能贡献给 $ans,ans+1$ 中的一个.
而 $10^{12}$ 以内的数字最多只会有不到十多个本质不同的质因子,所以可以枚举子集.
考虑枚举出 $A$ 和 $B$,令 $A\times x=ans,B\times y=ans+1$.
则需要满足 $Ax-By=-1,gcd(x,y)=1$
但其实我们发现 $gcd(x,y)=1$ 是不用判的,因位如果等式成立,则 $gcd(x,y)$ 就一定是 $1$.
那么,我们只需找到 $Ax-By=-1$ 的最小 $x$ 正整数解就行了.
这个可以用 $exgcd$ 直接求,但是有一些细节需要注意:

  1. $A,B$ 都需要大于 $0$.
  2. 我们想求 $x$,所以 $y$ 到底是多少我们是不关心的,直接无视掉就好.

Code: 

#include <cstdio>  
#include <vector> 
#include <algorithm>   
#define N 1000006 
#define inf 100000000000000000
#define ll long long 
#define LL long long
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
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);
    ll tmp=x;
    x=y,y=tmp-a/b*y;
    return ans;
}         
vector<ll>v;   
int tot; 
int prime[N],is[N];    
void init() 
{ 
    int i,j; 
    for(i=2;i<N;++i) 
    {
        if(!is[i]) prime[++tot]=i;         
        for(j=1;j<=tot&&1ll*prime[j]*i<1ll*N;++j) 
        {
            is[prime[j]*i]=1; 
            if(i%prime[j]==0) break;    
        }
    }    
}
ll solve(ll A,ll B) 
{    
    ll x,y,g,b,ans; 
    g = exgcd(A,B,x,y);                 
    if(-1%g) return inf;        
    x*=-1/g,y*=g;    
    B/=g; 
    if(B<0) B=-B;   
    ans=x%B;      
    if(ans<=0) ans+=B;  
    return ans*A; 
} 
int main() 
{ 
    // setIO("input");  
    init(); 
    int T,i,j; 
    scanf("%d",&T); 
    while(T--)
    {
        ll n,h,answer=inf; 
        scanf("%lld",&n),h=n;
        for(i=1;i<=tot;++i) 
        {
            if(h%prime[i]==0) 
            { 
                ll kk=1;    
                while(h%prime[i]==0) 
                { 
                    h/=prime[i];   
                    kk*=prime[i];    
                }      
                v.push_back(kk);    
            }       
        }
        if(h) v.push_back(h);                  
        int len=v.size();    
        for(i=0;i<(1<<len);++i) // 枚举所有子集 
        {  
            ll tmp=1; 
            for(j=0;(1<<j)<=i;++j) 
            {  
                if(i&(1<<j)) 
                    tmp*=v[j];         
            }
            ll A=tmp,B=2*n/tmp;     
            answer=min(answer,min(solve(A,B),solve(B,A)));  
        }
        printf("%lld\n",answer); 
        v.clear();    
    }
    return 0; 
}

  

推荐阅读