数论基础

听说明天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)?' ':'\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\n",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\n",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\n",(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\n",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\n",i*g-tmp);
            return;
        }
        cur=cur*bz;
    }
}
int main()
{
    scanf("%d%lld",&n,&p);
    readin(A); readin(B);
    bsgs();
}
posted @ 2016-04-15 23:44  fjzzq2002  阅读(469)  评论(0编辑  收藏  举报