Comet OJ - Contest #10 鱼跃龙门 exgcd+推导

匿名 (未验证) 提交于 2019-12-02 23:59:01

考试的时候推出来了,但是忘了 $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;  }

  

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!