• 数论基础


    听说明天AU爷ysy要给我们讲数论

    赶紧预习一下好了

    ①高斯消元

    嗯这个就不多解释了…

    题1 bzoj1013 球形空间产生器

    嗯这题就是给你一个n维球体上的n+1个点,求圆心。

    所以假设第i个点是(a[i][1],a[i][2]…a[i][n])

    那么我们就是要找一个点(x[1]…x[n])使得

    (a[i][1]-x[1])^2+(a[i][2]-x[2])^2+…+(a[i][n]-x[n])^2=r^2

    那么我们就有这样n+1个式子:

    (a[1][1]-x[1])^2+(a[1][2]-x[2])^2+…+(a[1][n]-x[n])^2=r^2
    (a[2][1]-x[1])^2+(a[2][2]-x[2])^2+…+(a[2][n]-x[n])^2=r^2
    ...
    (a[n+1][1]-x[1])^2+(a[n+1][2]-x[2])^2+…+(a[n+1][n]-x[n])^2=r^2

    那么我们把这n+1个式子两两对减,就会得到一坨类似这样的式子:

    $sum_{i=1}^n{(x[i]*2(a[2][i]-a[1][i]))}+sum_{i=1}^n(a[2][i]^2-a[1][i]^2)=0$

    然后高斯消元即可.

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <math.h>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    typedef double ld;
    #define SZ 233
    namespace Gauss
    {
    ld gd[SZ][SZ+1],ans[SZ];
    bool isfree[SZ];
    void gauss(int n)
    {
        int cur=1;
        for(int i=1;i<=n;i++)
        {
            isfree[i]=0;
            int tg=0;
            for(int j=cur;j<=n;j++) if(fabs(gd[j][i])>1e-6) {tg=j; break;}
            if(!tg) {isfree[i]=1; continue;}
            for(int j=1;j<=n;j++) swap(gd[cur][j],gd[tg][j]);
            for(int j=1;j<=n;j++)
            {
                if(j==cur||fabs(gd[j][i])<1e-6) continue;
                ld bs=gd[j][i]/gd[cur][i];
                for(int k=1;k<=n+1;k++) gd[j][k]-=bs*gd[cur][k];
            }
            ++cur;
        }
        for(int i=1;i<=n;i++)
        {
            if(isfree[i]) continue;
            for(int j=1;j<=n;j++)
            {
                if(fabs(gd[j][i])>1e-6) ans[i]=gd[j][n+1]/gd[j][i];
            }
        }
    }
    }
    int n;
    ld as[SZ][SZ];
    void pre()
    {
        scanf("%d",&n);
        for(int i=1;i<=n+1;i++)
        {
            for(int j=1;j<=n;j++) scanf("%lf",&as[i][j]);
            if(i==1) continue;
            for(int j=1;j<=n;j++)
            {
                Gauss::gd[i-1][j]=2*(as[i][j]-as[i-1][j]);
                Gauss::gd[i-1][n+1]+=as[i][j]*as[i][j]-as[i-1][j]*as[i-1][j];
            }
        }
    }
    int main()
    {
        using namespace Gauss;
        pre(); gauss(n);
        for(int i=1;i<=n;i++) printf("%.3lf%c",ans[i],(i!=n)?' ':'
    ');
    }

    ②exgcd

    这种傻逼玩意儿不用我说了吧

    求一组x,y使得ax+by=1。如果(a,b)≠1就无解。

    我们先求出bx'+(a%b)y'=1的解x'、y'。

    那么设a=pb+q(0<=q<b)。所以bx'+qy'=1。

    所以bx'+(a-pb)y'=1。

    所以ay'+b(x'-py')=1。

    即ay'+b(x'-(a/b)y')=1。

    这样就求出了一组可行解。

    代码稍后放出。

    ③BSGS

    正常的BSGS:求一个最小的非负数x使得a^x=b(mod c),其中c为质数。

    首先我们肯定知道x<c-1嘛

    额我们设x=pg+m(0<=m<g),这个g是啥等会儿再说。

    那么a^(pg+m)=b(mod c)

    我们稍微搞一下变成a^m=b*a^(-gp) (mod c)

    然后我们发现枚举一下m,然后把a^m map一下,右边枚举一下g,exgcd(费马小定理)一下,在map里面查一下就行了,第一个查到了显然就是最小的解。

    复杂度大概是O((g+c/g)logc)或者O(g+c/g),所以我们令g=sqrt(c)就可以做到科学的O(sqrt(c)logc)或O(sqrt(c))了。

    扩展的BSGS:(方老师写的写错了不要打我)

    image

    例二 bzoj2242 计算器

    搞这种三合一的题啊,excited

    似乎特判十分多的样子

    //By zzq
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <math.h>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    int T,k;
    ll qp(ll a,ll b,ll c)
    {
        if(b==0) return 1;
        ll hf=qp(a,b>>1,c);
        hf=hf*hf%c;
        if(b&1) hf=hf*a%c;
        return hf;
    }
    ll gcd(ll a,ll b) {return (b==0)?a:gcd(b,a%b);}
    //ax+by=1
    void exgcd(ll a,ll b,ll& x,ll& y)
    {
        if(b==0) {x=1; y=0; return;}
        exgcd(b,a%b,x,y);
        ll p=x-a/b*y; x=y; y=p;
    }
    void q1(ll a,ll b,ll c) {printf("%lld
    ",qp(a%c,b,c));}
    #define orzhzwer {puts("Orz, I cannot find x!"); return;}
    void q2(ll a,ll b,ll c)
    {
        ll g=gcd(a,c);
        if(b%g) orzhzwer
        a/=g; b/=g; c/=g;
        ll x,y; exgcd(a,c,x,y);
        x=x*b%c; x=(x%c+c)%c;
        printf("%lld
    ",x);
    }
    map<ll,ll> mp;
    void q3(ll a,ll b,ll c)
    {
        a%=c; b%=c;
        if(!a&&!b) {puts("1"); return;}
        if(!a) orzhzwer
        mp.clear();
        ll m=sqrt(c); ++m;
        ll cur=1; mp[1]=m+233;
        for(int i=1;i<m;i++)
        {
            cur=cur*a%c;
            if(!mp[cur]) mp[cur]=i;
        }
        ll tm=b%c,bz=qp(a%c,(c-2)*m%(c-1),c);
        for(int i=0;i<m;i++)
        {
            long long tmp=mp[tm];
            if(tmp)
            {
                if(tmp==m+233) tmp=0;
                printf("%lld
    ",(i*m+tmp)%(c-1));
                return;
            }
            tm=tm*bz%c;
        }
        orzhzwer
    }
    int main()
    {
        int T,k;
        scanf("%d%d",&T,&k);
        while(T--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(k==1) q1(a,b,c);
            else if(k==2) q2(a,b,c);
            else q3(a,b,c);
        }
    }

    ④矩阵求逆

    只要在高斯消元的时候旁边放一个单位矩阵,把原矩阵消元成单位矩阵,同时把单位矩阵也一起拿去变换就行了。注意取模的时候负数要判一下。

    例3 bzoj4128 Matrix

    BSGS+矩阵求逆的好(裸?)题

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <math.h>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    int n;
    typedef long long ll;
    ll p;
    struct mat{ll s[72][72];}A,B;
    ll qp(ll a,ll b)
    {
        if(b==0) return 1;
        ll hf=qp(a,b>>1);
        hf=hf*hf%p;
        if(b&1) hf=hf*a%p;
        return hf;
    }
    void readin(mat& x)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++) scanf("%lld",&x.s[i][j]), x.s[i][j]%=p;
        }
    }
    mat dw()
    {
        mat a;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                a.s[i][j]=(i==j);
        return a;
    }
    mat inv(mat x)
    {
        mat a=dw();
        for(int i=1;i<=n;i++)
        {
            int tg=-1;
            for(int j=i;j<=n;j++)
            {
                if(x.s[j][i]) {tg=j; break;}
            }
            if(tg==-1) {printf("Fuck You"); exit(-1);}
            for(int j=1;j<=n;j++) swap(x.s[tg][j],x.s[i][j]), swap(a.s[tg][j],a.s[i][j]);
            ll cp=qp(x.s[i][i],p-2);
            for(int j=1;j<=n;j++) x.s[i][j]=(x.s[i][j]*cp)%p, a.s[i][j]=(a.s[i][j]*cp)%p;
            for(int j=1;j<=n;j++)
            {
                if(i==j) continue;
                ll bs=x.s[j][i]%p;
                for(int k=1;k<=n;k++)
                {
                    x.s[j][k]=(x.s[j][k]-bs*x.s[i][k]%p)%p;
                    x.s[j][k]=(x.s[j][k]+p)%p;
                    a.s[j][k]=(a.s[j][k]-bs*a.s[i][k]%p)%p;
                    a.s[j][k]=(a.s[j][k]+p)%p;
                }
            }
        }
        return a;
    }
    mat operator * (mat a,mat b)
    {
        mat ans;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                ll ap=0;
                for(int k=1;k<=n;k++) ap=(ap+a.s[i][k]*b.s[k][j]%p)%p;
                ans.s[i][j]=ap;
            }
        }
        return ans;
    }
    mat qp(mat a,ll cf)
    {
        mat ans=dw();
        for(;cf;cf>>=1)
        {
            if(cf&1) ans=ans*a;
            a=a*a;
        }
        return ans;
    }
    const int hn=7; //强行卡常 
    ll gh(mat x)
    {
        ll hash=0; int cc=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                hash=hash*199999+x.s[i][j];
                if((++cc)>=hn) return hash;
            }
        }
        return hash;
    }
    map<ll,int> mp;
    //这是我见过最难写的bsgs! 
    void bsgs()
    {
        int g=sqrt(p); ++g; mp.clear();
        mat cur=dw(); mp[gh(cur)]=g+233;
        for(int i=1;i<g;i++)
        {
            cur=cur*A;
            ll th=gh(cur);
            if(!mp[th]) mp[th]=i;
        }
        cur=B; mat bz=inv(qp(A,g));
        for(int i=0;i<g;i++)
        {
            ll tmp=mp[gh(cur)];
            if(tmp)
            {
                if(tmp==g+233) tmp=0;
                printf("%lld
    ",i*g+tmp);
                return;
            }
            cur=cur*bz;
        }
    }
    int main()
    {
        scanf("%d%lld",&n,&p);
        readin(A); readin(B);
        bsgs();
    }

    至于这样写好不好写…

    image

    下面这份是矩阵求逆的…上面这份是比较科学的做法…嗯我们比较一下代码长度…

    那科学的做法是什么呢?

    我们不妨再回顾一下bsgs的过程…

    我们设x=pg+m(0<=m<g),其中g≈sqrt(p)…

    嗯…这样就得求逆元对吧

    现在我们不妨换一个思路!我们设x=pg-m(0<=m<g),其中g≈sqrt(p)+2…

    这样原来a^x=b变成了a^(pg)=b*a^m,这样就不用写什么鬼逆元啦!@24O5U4PTZTP(KLQIT3YP0E

    感觉我bsgs顿时白学了

    比较科学的代码:

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <math.h>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    int n;
    typedef long long ll;
    ll p;
    struct mat{ll s[72][72];}A,B;
    ll qp(ll a,ll b)
    {
        if(b==0) return 1;
        ll hf=qp(a,b>>1);
        hf=hf*hf%p;
        if(b&1) hf=hf*a%p;
        return hf;
    }
    void readin(mat& x)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) scanf("%lld",&x.s[i][j]), x.s[i][j]%=p;
    }
    mat dw()
    {
        mat a;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                a.s[i][j]=(i==j);
        return a;
    }
    mat operator * (mat a,mat b)
    {
        mat ans;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                ll ap=0;
                for(int k=1;k<=n;k++) ap=(ap+a.s[i][k]*b.s[k][j]%p)%p;
                ans.s[i][j]=ap;
            }
        }
        return ans;
    }
    mat qp(mat a,ll cf)
    {
        mat ans=dw();
        for(;cf;cf>>=1)
        {
            if(cf&1) ans=ans*a;
            a=a*a;
        }
        return ans;
    }
    const int hn=7; //强行卡常 
    ll gh(mat x)
    {
        ll hash=0; int cc=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                hash=hash*199999+x.s[i][j];
                if((++cc)>=hn) return hash;
            }
        }
        return hash;
    }
    map<ll,int> mp;
    //这是我见过最难写的bsgs! 
    void bsgs()
    {
        int g=sqrt(p)+2; mp.clear();
        mat cur=B; mp[gh(cur)]=g+233;
        for(int i=1;i<g;i++)
        {
            cur=cur*A; mp[gh(cur)]=i;
        }
        cur=dw(); mat bz=qp(A,g);
        for(int i=0;i<g;i++)
        {
            ll tmp=mp[gh(cur)];
            if(tmp)
            {
                if(tmp==g+233) tmp=0;
                printf("%lld
    ",i*g-tmp);
                return;
            }
            cur=cur*bz;
        }
    }
    int main()
    {
        scanf("%d%lld",&n,&p);
        readin(A); readin(B);
        bsgs();
    }
  • 相关阅读:
    OpenStack(queens)最小化搭建记录——控制与计算共两个节点
    CentOS7 minimal(最小化安装)后增加的软件安装
    redis
    layui 单击事件,选中 checkbox f
    DLL 引用查询工具
    自定义JS Map 函数
    修改MSSQL字段类型
    苹果ATS特性服务器配置指南 HTTPS 安卓可以用 IOS 报错。
    基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
    不能将多个项传入“Microsoft.Build.Framework.ITaskItem”类型的参数
  • 原文地址:https://www.cnblogs.com/zzqsblog/p/5397304.html
Copyright © 2020-2023  润新知