2019年9月15日——数论专题课程总结(day2)

毒瘤\(xgzc,QwQ\)~~~~~~

\(PartA:gcd\)

额,具体思想与证明就不说了,这都不知道的话就退役去向欧几里得学数学吧……

inline int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}

\(extend:exgcd\)

额,这也不用证吧,这不就\(yy\)一下就出来了吗~~
有解充要条件:

\[\text{在}~ ~ ~ax+by=c~ ~ ~\text{中有}~ ~ ~gcd(a,b)|c\]

不妨设\(ax+by=gcd(a,b)\),则有:
\[ax+by=gcd(a,b)=bx^{'}+(a~mod~b)y^{'}=gcd(b,a~mod~b)\]
移一下项就可以递归搞了:

int x,y;
inline void exgcd(int a,int b)
{
    if(!b)
    {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b);
    int tmp=x;
    x=y;
    y=tmp-a/b*y;
}

模板:青蛙的约会:

#include<bits/stdc++.h>
using namespace std;
#define in(a) a=read()
#define ll long long
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
ll jie[2];
inline ll gca(ll s,ll t)
{
    if(t==0) return s;
    return gca(t,s%t);
}
inline void getit(ll s,ll t)
{
    if(t==0)
    {
        jie[0]=1;
        jie[1]=0;
        return;
    }
    getit(t,s%t);
    ll u=jie[1],v=jie[0]-s/t*jie[1];
    jie[0]=u;
    jie[1]=v;
}
int main()
{
    ll n,x,y,a,b;
    in(x);in(y);in(a);in(b);in(n);
    if(a<b) swap(a,b),swap(x,y);
    ll delta=y-x;
    ll q=gca(a-b,n);
    if(delta%q!=0)
    {
        printf("Impossible\n");
        return 0;
    }
    getit(a-b,n);
    ll ans=jie[0]*delta/q;
    ll r=n/q;
    ans=(ans%r+r)%r;
    printf("%lld\n",ans);
    return 0;
}

\(PartB:\)费马小定理和欧拉定理

费马小定理:
\[a^{p-1}\equiv1\pmod{p}~~~ (d_p==1)\]

欧拉定理:
\[a^b\equiv a^{b\%\phi(n)}\pmod{n}\]

\(PartC:BSGS\)

主要功能是用于求解类似如下方程的解:
\[a^x\equiv b\pmod{c}\]
主元当然是\(x\)\(QwQ\)~~~

额,面对柿子一开始就很懵逼,现在依旧……

好吧,现在蒟蒻的我依旧只会做法,希望将来有一天能够弄得透彻一点:

\(m=\lceil \sqrt{p}\rceil,x=my+z(y,z\text{均为变量})\),则当\(a^x\equiv b\pmod{c}\)成立时有\(a^{my}* a^z\equiv b\pmod{c}\),移一下项就有\(a^{my}\equiv \frac{b}{a^z}\pmod{c}\),预处理出\(\forall z\in\left[1,m\right]\)\(\frac{b}{a^z}\)并存在\(map\)里面,然后枚举\(y\)判有无解即可。

时间复杂度\(O(\sqrt{n})\)

inline int sol(int a,int b,int c)
{
    if(!(a%c)) return -1;
    int ans=b%c,m=ceil(sqrt(c));
    mp[ans]=0;
    fur(i,1,m)
    {
        ans=ans*a%c;
        mp[ans]=i;
    }
    int tmp=power(a,m,c);ans=1;
    fur(i,1,m)
    {
        ans=ans*tmp%c;
        if(mp[ans]) return (i*m-mp[ans])%c+c)%c;
    }
    return -1;
}

模板:\(Discrete~Logging\)

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
    int x=0;
    char ch=getchar();
    for(;!isalnum(ch);ch=getchar());
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x;
}
map <int,int> mp;
inline int power(int x,int k,int mod)
{
    int res=1;
    for(;k;k>>=1,x=x*x%mod) if(k&1) res=res*x%mod;
    return res;
}
inline void sol(int a,int b,int c)
{
    if(!(a%c))
    {
        puts("no solution");
        return;
    }
    int ans=b%c,m=ceil(sqrt(c));
    bool flag=false;
    mp[ans]=0;
    fur(i,1,m)
    {
        ans=ans*a%c;
        mp[ans]=i;
    }
    int tmp=power(a,m,c);ans=1;
    fur(i,1,m)
    {
        ans=ans*tmp%c;
        if(mp[ans])
        {
            printf("%lld\n",((i*m-mp[ans])%c+c)%c);
            flag=true;
            break;
        }
    }
    if(!flag) puts("no solution");
}
jinitaimei main()
{
    int a,b,c;
    while(scanf("%lld%lld%lld",&c,&a,&b)!=EOF)
    {
        mp.clear();
        sol(a,b,c);
    }
    return 0;
}

\(extend:exbsgs\)(不是质数的情况)
这个\(xg\)说有点毒瘤,那就直接上柿子算了:
\[a^x\equiv b\pmod c\Rightarrow a^x+kc=b\]
\[\text{令}~g=gcd(a,c),\therefore\frac{b}{g}\in N^{* }\]
\[\Rightarrow \frac{a}{g}a^{x-1}+k\frac{p}{g}\equiv\frac{b}{g}\pmod{\frac{p}{g}}\]
\[\Rightarrow a^{x-1}\equiv \frac{b}{g}(\frac{a}{g})^{-1}\pmod{\frac{p}{g}}\]
\[\Rightarrow a^{x^{'}}\equiv b^{'}\pmod{m^{'}}(x^{'}=x-1,b^{'}=\frac{b}{g}(\frac{a}{g})^{-1},m^{'}=\frac{p}{g})\]
用类似于\(exgcd\)的思想递归即可

\(PartD:crt\)

这个是用来求同余方程组的:
\[\begin{cases}x\equiv a_1\pmod{p_1}\\x\equiv a_2\pmod{p_2}\\……\\x\equiv a_n\pmod {p_n}\end{cases}\]
其中\(p_i\)都是质数

貌似有个利用的栗子是韩信点兵??

还是说正常的吧:

(没模板题,就不给代码了)
\[\text{设}M=\prod\limits_{i=1}^n p_i,m_i=\frac{M}{p_i},t_i\equiv \frac{1}{m_i}\pmod{p_i}\]
\[\text{那么所求为}ans\equiv\sum a_it_im_i\pmod{M}\]

\(extend:excrt\text{(话说好多ex-QwQ)}\):

不是质数的情况有点难受

考虑两两合并方程,利用\(exgcd\)即可:

const int xx=1e5+1612;
int t[xx],p[xx];
int k1,k2,x,n,M;
inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}
inline void exgcd(int a,int b)
{
    if(!b)
    {
        k1=1,k2=0;
        return;
    }
    exgcd(b,a%b);
    int tmp=k1;
    k1=k2;k2=tmp-a/b*k2;
}
inline int Fast_mul(int a,int b,int mod)
{
    int res=0;
    for(;b;b>>=1,a=(a+a)%mod) if(b&1) res=(res+a)%mod;
    return res;
}
inline void excrt()
{
    M=p[1];x=t[1];
    fur(i,2,n)
    {
        int a=M,b=p[i];
        int c=(t[i]-x%b+b)%b;
        int tmp=gcd(a,b);
        exgcd(a,b);
        int lcm=b/tmp;
        k1=Fast_mul(k1,c/tmp,lcm);
        x+=k1*M;
        M*=lcm;
        x=(x%M+M)%M;
    }
    printf("%lld\n",x);
}

模板:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
    int x=0;
    char ch=getchar();
    for(;!isalnum(ch);ch=getchar());
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x;
}
const int xx=1e5+1612;
int t[xx],p[xx];
int k1,k2,x,n,M;
inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}
inline void exgcd(int a,int b)
{
    if(!b)
    {
        k1=1,k2=0;
        return;
    }
    exgcd(b,a%b);
    int tmp=k1;
    k1=k2;k2=tmp-a/b*k2;
}
inline int Fast_mul(int a,int b,int mod)
{
    int res=0;
    for(;b;b>>=1,a=(a+a)%mod) if(b&1) res=(res+a)%mod;
    return res;
}
inline void sol()
{
    M=p[1];x=t[1];
    fur(i,2,n)
    {
        int a=M,b=p[i];
        int c=(t[i]-x%b+b)%b;
        int tmp=gcd(a,b);
        exgcd(a,b);
        int lcm=b/tmp;
        k1=Fast_mul(k1,c/tmp,lcm);
        x+=k1*M;
        M*=lcm;
        x=(x%M+M)%M;
    }
    printf("%lld\n",(x%M+M)%M);
}
inline void init()
{
    n=in;
    fur(i,1,n) p[i]=in,t[i]=in;
}
jinitaimei main()
{
    init();
    sol();
    return 0;
}

屠龙勇士:

思路还蛮有意思的,需要看清楚题,分两种情况(可用\(excrt\)的与不可用但不需用的),然后列式子,化简方程,最后用模板搞事情就可以啦:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define jinitaimei signed
#define int long long
const int xx=1e5+1612;
int t[xx],p[xx],cnt;
int k1,k2,x,n,M;
int tmp1[xx],tmp2[xx],tmp3[xx],tmp4[xx];
bool flag;
multiset <int> s;
multiset <int>:: iterator it;
inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}
inline void exgcd(int a,int b)
{
    if(!b)
    {
        k1=1,k2=0;
        return;
    }
    exgcd(b,a%b);
    int tmp=k1;
    k1=k2;
    k2=tmp-a/b*k2;
}
inline int Fast_mul(int a,int b,int mod)
{
    int res=0;
    if(b<0) return 1;
    for(;b;b>>=1,a=(a+a)%mod) if(b&1) res=(res+a)%mod;
    return res;
}
inline void sol()
{
    if(flag) return;
    M=p[1];x=t[1];
    fur(i,2,cnt)
    {
        int a=M,b=p[i];
        int c=((t[i]-x%b+b)%b+b)%b;
        int tmp=gcd(a,b);
        exgcd(a,b);
        int lcm=b/tmp;
        if(c%tmp)
        {
            puts("-1");
            return;
        }
        k1=Fast_mul(k1,c/tmp,lcm);
        x+=k1*M;
        M*=lcm;
        x=(x%M+M)%M;
    }
    printf("%lld\n",(x%M+M)%M);
}
inline void init()
{
    s.clear();
    flag=false;cnt=0;
    int tmp=0,m;
    scanf("%lld%lld",&n,&m);
    fur(i,1,n) scanf("%lld",&tmp1[i]);
    fur(i,1,n){scanf("%lld",&tmp2[i]);if(tmp2[i]<tmp1[i]) flag=true;}
    fur(i,1,n) scanf("%lld",&tmp4[i]);
    fur(i,1,m) scanf("%lld",&tmp3[i]);
    fur(i,1,m) s.insert(tmp3[i]);
    fur(i,1,n)
    {
        it=(tmp1[i]<(*s.begin()))?s.begin():(--s.upper_bound(tmp1[i]));
        tmp3[i]=*it;
        s.erase(it);
        s.insert(tmp4[i]);
    }
    if(flag)
    {
        fur(i,1,n) tmp=max(tmp,(int)ceil((double)tmp1[i]/(double)tmp3[i]));
        printf("%lld\n",tmp);
    }
}
inline void handle()
{
    if(flag) return;
    fur(i,1,n)
    {
        if(tmp1[i]==tmp3[i]) continue;
        int tmp=gcd(tmp3[i],tmp2[i]);
        exgcd(tmp3[i],tmp2[i]);
        if(tmp1[i]%tmp)
        {
            puts("-1"),flag=true;
            return;
        }
        k1=Fast_mul(k1,tmp1[i]/tmp,tmp2[i]/tmp);
        t[++cnt]=k1;
        p[cnt]=tmp2[i]/tmp;
    }
}
jinitaimei main()
{
    int T;
    scanf("%lld\n",&T);
    while(T--)
    {
        init();
        handle();
        sol();
    }
    return 0;
}

\(PartE:Lucas\)

给个式子就好了,相信你们都会,逃……(\(p\)是质数):
\[C^m_n\equiv C_{n\%p}^{m\%p}* C_{n/p}^{m/p}\pmod{p}\]

模板:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline ll power(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b>0)
    {
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
ll jc[250000];
inline ll lucas(ll m,ll n,ll mod)
{
    if(m>n) return 0;
    if(m==0) return 1;
    if(m<mod&&n<mod) return jc[n]*power(jc[m]*jc[n-m]%mod,mod-2,mod)%mod;
    return lucas(m/mod,n/mod,mod)*lucas(m%mod,n%mod,mod)%mod;
}
int main()
{
    ll t;t=in;
    while(t--)
    {
        ll n,m,p;
        n=in;m=in;p=in;
        jc[0]=1;
        fur(i,1,n+m) jc[i]=i*jc[i-1]%p;
        printf("%lld\n",lucas(m,n+m,p));
    }
    return 0;
}

\(extend:exLucas\)
这个需要不厌其烦地用\(crt\)去分模数使得式子可以满足普通式的条件

暂时太菜,就不给代码了(真的不是我不会)

\(PartF:\text{二次剩余}\)

时间原因,还要改考试题,放个大佬博客在这里先,下次再来写

模板&模板题解:

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define re register
#define fur(i,a,b) for(re int i=a;i<=b;++i)
#define fdr(i,a,b) for(re int i=a;i>=b;--i)
#define cl(a,b) memset(a,b,sizeof(a))
#define jinitaimei signed
#define int long long
inline int read()
{
    int x=0;
    char ch=getchar();
    for(;!isalnum(ch);ch=getchar());
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x;
}
struct num
{
    int x,y;
};
int w,q;
inline num times(num a,num b,int p)
{
    num ans;
    ans.x=(a.x*b.x%p+a.y*b.y%p*w%p+p)%p;
    ans.y=(a.y*b.x%p+a.x*b.y%p+p)%p;
    return ans;
}
inline int pow1(num a,int b,int p)
{
    num ans={1,0};
    for(;b;b>>=1,a=times(a,a,p)) if(b&1) ans=times(ans,a,p);
    return ans.x%p;
}
inline int pow2(int a,int b,int p)
{
    int ans=1;
    for(;b;b>>=1,a=a*a%p) if(b&1) ans=ans*a%p;
    return ans%p;
}
inline int sol(int n,int p)
{
    n%=p;
    if(pow2(n,(p-1)/2,p)==p-1) return -1;
    while(true)
    {
        q=rand()%p;
        w=(q*q%p-n%p+p)%p;
        if(pow2(w,(p-1)/2,p)==p-1) break;
    }
    return pow1((num){q,1},(p+1)/2,p);
}
jinitaimei main()
{
    int T=in;
    while(T--)
    {
        int n=in,p=in;
        if(!n)
        {
            puts("0");
            continue;
        }
        int ans1=sol(n,p),ans2=p-ans1;
        if(ans1==-1) puts("Hola!");
        else
        {
            if(ans1==ans2) printf("%lld\n",ans1);
            else if(ans1<ans2) printf("%lld %lld\n",ans1,ans2);
            else printf("%lld %lld\n",ans2,ans1);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ALANALLEN21LOVE28/p/11564866.html