zoukankan      html  css  js  c++  java
  • 关于数论的一些总结

    结论:我数论太渣了……

    言归正传……先列出几个常用的性质/结论

    同余式:

    1. da≡db (mod m) 则a≡b (mod m/(m,d) ) (这在取遍剩余系会用到)

    2. a≡b (mod m)  m'|m , a≡b (mod m')

    3. a≡b (mod mi) i=1..k 等价于 a≡b (mod M) M=[m1,m2,..mk]

    一般剩余系:

    剩余系就是可能余数组成的集合,简化剩余系也称既约剩余系,是模n的完全剩余系的一个子集,其中每个元素与n互素。

    1. 模p的剩余系{rk},若(p,d)=1那么{d*rk}也是模p的剩余系

    这个很重要,如果求ax mod p的既约剩余系的大小,我们只要利用同余式的性质1

    转化为x*a/gcd(a,p) mod (p/gcd(a,p)),根据这个理论 x取值个数就是p/gcd(a,p)

    2. 设模m1的既约剩余系为R1,m2的既约剩余系为R2,m1m2互质,则模m1m2的既约剩余系为R={x=m2x1+m1x2 (mod m1m2) | x1∈R1,x2∈R2}

    这个结论证明可以考虑任意两个x互不同余即可(作差判断)

    这就告诉我们一个很厉害的东西 S(M)表示M既约剩余系的大小的话,S(M)=S(m1)*S(m2)

    3. 这个结论推广到k维就是,设模mk的既约剩余系为Rk, mk两两互质,M=∏mi 则模M的既约剩余系为R={ ∑M*mi-1*ai (mod M) | xi∈Ri }  

    因此以后我们求模M的剩余系大小,可以对M质因数分解分别求出后再相乘

    阶、原根、指标:

     阶(又叫指数):a,m为互素整数且m>=2 ,则使 ar≡1(mod m) 成立的最小正整数 r 记为 a 模 m 的指数,记作ord_m(a)

    根据欧拉定理,显然ord_m(a)一定是φ(m)的约数

    阶有几个性质: 1. ord_m(a)=xy 则 ord_m(ax)=y 2.ord_m(a)=x, ord_m(b)=y (x,y)=1 则ord_m(ab)=xy;

    3. ord_m(a)=t ord_m(ax)=t/gcd(t,x) (其实就是性质1的变形)

    原根:阶中要求(a,m)互素,所以a^x与m互素,所以ax模m的余数与m互素,这样这个余数只可能有 φ(m) 种不同的取值。

    (这里我想到了了一个很有意思的结论:1~n内与n互质的数的和为{n*φ(n)+[n==1]} /2 

    这里考虑gcd(i,n)=1,则gcd(n,n-i)=1,证明用反证法即可。)

    当满足ord_m(a)=φ(m)的a称作模m的原根,记作g。

    这有一个显然的结论若 x=0,1,2,3,...,φ(m)-1,则 gx 模 m 的余数都和 m 互素,并且模 m 两两不同余。

    指标:简单来说,就是一切小于m且与m互质的数r,都存在唯一个数k(k<φ(m)),使得gk≡r (mod m)

    我们就把k叫作r的指标,记作I(r)

    这是一个非常有用的性质,之后的题目我们会经常看到他。

    指标有这么几个性质:1. I(ab)≡I(a)+I(b) (mod m) 2. I(ak)≡kI(a) (mod m)

    简而言之,指标就像是数论里的取对数。

    原根和求指标将在后面的同余方程中起到巨大作用,但是首先我们要知道什么数有原根

    直接上结论: 当且仅当m=2,4,pk,2pk时才有原根,p是某一奇素数

    证明可以看《初等数论及其应用》

    还有一个非常有用的定理是:若g是某一奇素数p的原根,那么g也是pk的原根,证明依然在《初等数论及其应用》

    事实上,一个数p的原根的数目为φ(φ(p)) (考虑若g是p的原根,gu是p的原根当且仅当gcd(u,φ(p))=1)

    另外我们注意到2的原根是1,4的原根是3,但其他2次幂是没有原根的(在高次剩余里,模2次幂会带来不小的麻烦)

    事实上有一个恒等式:aφ(xy)/2≡1 (mod xy) (x,y互素且a与xy互素)

    在2次幂的时候就有a^(2k)≡1 (mod 2k+2) 恒成立,所以2的3次及以上次幂没有原根

    (补充一个很有意思的结论:5模2k+2的指数正好是2k

    一个数的原根不止一个,一般就找最小的那个就可以了

    其他的原根可用最小原根的gu表示(gu是p的原根当且仅当gcd(u,φ(p))=1)

    找m的最小原根一般就是暴力枚举g,然后穷举m的每个质因数p,判断gφ(m)/p≡1 (mod m)是否成立(成立的话就不是原根)

    一道找所有原根的题目

      1 #include<bits/stdc++.h>
      2 
      3 using namespace std;
      4 typedef long long ll;
      5 int phi[1000010],p[1000010],a[100010],ans[1000010],n,r,t;
      6 bool v[1000010];
      7 
      8 int gcd(int a,int b)
      9 {
     10     return (b==0)?a:gcd(b,a%b);
     11 }
     12 
     13 ll quick(ll x,int y,int mo)
     14 {
     15     ll s=1;
     16     while (y)
     17     {
     18         if (y&1) s=s*x%mo;
     19         x=x*x%mo;
     20         y>>=1;
     21     }
     22     return s;
     23 }
     24 
     25 int check(int n)
     26 {
     27     if (n%2==0) return -1;
     28     if (!v[n]) return n;
     29     for (int i=2; p[i]*p[i]<=n; i++)
     30         if (n%p[i]==0)
     31         {
     32             while (n%p[i]==0) n/=p[i];
     33             if (n>1) return -1;
     34             else return p[i];
     35         }
     36     return -1;
     37 }
     38 
     39 bool work(int g,int mo)
     40 {
     41     if (gcd(g,mo)!=1) return 0;
     42     for (int i=1; i<=r; i++)
     43         if (quick(g,phi[mo]/a[i],mo)==1) return 0;
     44     return 1;
     45 }
     46 
     47 int getg(int mo)
     48 {
     49     int n=phi[mo];
     50     r=0;
     51     for (int i=1; p[i]*p[i]<=n; i++)
     52         if (n%p[i]==0)
     53         {
     54             while (n%p[i]==0) n/=p[i];
     55             a[++r]=p[i];
     56         }
     57     if (n>1) a[++r]=n;
     58     for (int i=2; i<mo; i++)
     59         if (work(i,mo)) return i;
     60 }
     61 
     62 int main()
     63 {
     64     phi[1]=1;
     65     for (int i=2; i<=1000000; i++)
     66     {
     67         if (!v[i])
     68         {
     69             phi[i]=i-1;
     70             p[++t]=i;
     71         }
     72         for (int j=1; j<=t; j++)
     73         {
     74             if (i*p[j]>1000000) break;
     75             v[i*p[j]]=1;
     76             if (i%p[j]==0)
     77             {
     78                 phi[i*p[j]]=phi[i]*p[j];
     79                 break;
     80             }
     81             else phi[i*p[j]]=phi[i]*(p[j]-1);
     82         }
     83     }
     84     while (scanf("%d",&n)!=EOF)
     85     {
     86         if (n==2)
     87         {
     88             puts("1");
     89             continue;
     90         }
     91         if (n==4)
     92         {
     93             puts("3");
     94             continue;
     95         }
     96         int ch=(n%2==1)?check(n):check(n/2);
     97         if (ch==-1)
     98         {
     99             puts("-1");
    100             continue;
    101         }
    102         int g=getg(n);
    103         int l=0;
    104         for (int i=1; i<=phi[n]; i++)
    105             if (gcd(i,phi[n])==1) ans[++l]=quick(g,i,n);
    106         sort(ans+1,ans+1+l);
    107         for (int i=1; i<=l; i++)
    108         {
    109             printf("%d",ans[i]);
    110             if (i==l) puts(""); else printf(" ");
    111         }
    112     }
    113 }
    hdu4992

    高次同余相关的问题:

    高次同余问题一般都会把余数b进行ga≡b (mod p)这样的变化

    这样统一底后进行两边取指标,这样就将问题转化为了线性同余问题

    比如今年第二场多校的hdu6051,就利用了将高次同余都转化为原根为底的形式

    具体题解在:http://bestcoder.hdu.edu.cn/blog/2017-multi-university-training-contest-2-solutions-by-%E7%94%B5%E5%AD%90%E7%A7%91%E6%8A%80%E5%A4%A7%E5%AD%A6/

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 typedef long long ll;
     5 const int mo=1e9+7;
     6 int m,p;
     7 ll sqr(ll x)
     8 {
     9     return x*x%mo;
    10 }
    11 
    12 void inc(int &a,int b)
    13 {
    14     a+=b;
    15     if (a>mo) a-=mo;
    16 }
    17 
    18 int phi(int n)
    19 {
    20     int s=n;
    21     for (int i=2; i*i<=n; i++)
    22         if (n%i==0)
    23         {
    24             s=s/i*(i-1);
    25             n/=i;
    26             while (n%i==0) n/=i;
    27         }
    28     if (n>1) s=s/n*(n-1);
    29     return s;
    30 }
    31 
    32 int calc(int n)
    33 {
    34     if (n==1) return 1;
    35     return 1ll*n*phi(n)/2%mo;
    36 }
    37 
    38 int main()
    39 {
    40     int cas;
    41     scanf("%d",&cas);
    42     for (int tt=1; tt<=cas; tt++)
    43     {
    44         scanf("%d%d",&m,&p);
    45         int ans=0;
    46         for (int i=1; i*i<=(p-1); i++)
    47             if ((p-1)%i==0)
    48             {
    49                 inc(ans,sqr(i)*calc((p-1)/i)%mo);
    50                 int j=(p-1)/i;
    51                 if (j!=i) inc(ans,sqr(j)*calc((p-1)/j)%mo);
    52             }
    53         int s=1ll*(p-1)*p/2%mo;
    54         ans=(ans-s+mo)%mo;
    55         ans=1ll*ans*m%mo;
    56         printf("Case #%d: %d
    ",tt,ans);
    57     }
    58 }
    hdu6051

    还有著名的数论之神,题解我是看这的:http://blog.csdn.net/regina8023/article/details/44863519

    (可以注意到模数是奇数,也就是不会考虑到2次幂的情况,降低了难度)

      1 #include<bits/stdc++.h>
      2 
      3 using namespace std;
      4 typedef long long ll;
      5 const int inf=1e9+7;
      6 int a[100010],A,B,n,t;
      7 map<int,int> mp;
      8 ll quick(ll x,int y,int mo)
      9 {
     10     ll s=1;
     11     while (y)
     12     {
     13         if (y&1) s=s*x%mo;
     14         x=x*x%mo;
     15         y>>=1;
     16     }
     17     return s;
     18 }
     19 
     20 int gcd(int a,int b)
     21 {
     22     return (b==0)?a:gcd(b,a%b);
     23 }
     24 
     25 int chai(int n,int p)
     26 {
     27     int s=0;
     28     while (n%p==0)
     29     {
     30         s++;
     31         n/=p;
     32     }
     33     return s;
     34 }
     35 
     36 void exgcd(int a, int b, int &x, int &y)
     37 {
     38      if (!b){x=1; y=0; return;}
     39      else {
     40           exgcd(b,a%b,x,y);
     41           int xx=x, yy=y;
     42           x=yy; y=xx-a/b*yy;
     43      }
     44 }
     45 
     46 int bsgs(int y,int z,int p)
     47 {
     48     y%=p; z%=p;
     49     mp.clear();
     50     mp[1]=p-1;
     51     ll m=(int)sqrt(p)+1, now=1;
     52     for (int i=1; i<=m; i++)
     53     {
     54         now=1ll*now*y%p;
     55         if (!mp[now]) mp[now]=i;
     56     }
     57     int g=gcd(now,p);
     58     if (g!=1) return -1;
     59     now/=g; int w=p/g,x,k;
     60     exgcd(now,w,x,k); x=(x+w)%w;
     61     ll step=z;
     62     for (int i=0; i<m; i++)
     63     {
     64         if (mp[step])
     65         {
     66            if (step==1) mp[step]=0;
     67            return mp[step]+i*m;
     68         }
     69         step=step*(ll)x%p;
     70     }
     71     return -1;
     72 }
     73 
     74 bool check(int g,int p)
     75 {
     76     for (int i=1; i<=t; i++)
     77         if (quick(g,(p-1)/a[i],p)==1) return 0;
     78     return 1;
     79 }
     80 
     81 int getg(int p)
     82 {
     83     t=0; int n=p-1;
     84     for (int i=2; i*i<=n; i++)
     85         if (n%i==0)
     86         {
     87             a[++t]=i;
     88             while (n%i==0) n/=i;
     89         }
     90     if (n>1) a[++t]=n;
     91     for (int i=2;;i++)
     92         if (check(i,p)) return i;
     93 }
     94 
     95 int calc(int p,int t,int nw,int B)
     96 {
     97     if (B==0)
     98     {
     99         int s=(t-1)/A+1;
    100         return quick(p,t-s,inf);
    101     }
    102     else {
    103         int s=chai(B,p),f=1;
    104         if (s)
    105         {
    106             if (s%A!=0) return 0;
    107             f=quick(p,s-s/A,inf);
    108             int gg=gcd(nw,B);
    109             nw/=gg; B/=gg;
    110         }
    111         int phi=nw/p*(p-1);
    112         int nb=bsgs(getg(p),B,nw);
    113         if (nb==-1) return 0;
    114         int gg=gcd(phi,A);
    115         if (nb%gg) return 0;
    116         return f*gg;
    117     }
    118 }
    119 
    120 int main()
    121 {
    122     int cas;
    123     scanf("%d",&cas);
    124     while (cas--)
    125     {
    126         scanf("%d%d%d",&A,&B,&n);
    127         n=2*n+1; B%=n;
    128         int ans=1;
    129         for (int i=2; i*i<=n; i++)
    130             if (n%i==0)
    131             {
    132                 int s=0,nw=1;
    133                 while (n%i==0)
    134                 {
    135                     s++; nw*=i;
    136                     n/=i;
    137                 }
    138                 ans*=calc(i,s,nw,B%nw);
    139                 if (!ans) break;
    140             }
    141         if (n>1) ans*=calc(n,1,n,B%n);
    142         printf("%d
    ",ans);
    143     }
    144 }
    bzoj2219

    前面的几题是高次同余方程的问题,那么高次剩余系的问题呢

    下面我觉得是一道最具有代表性的题目:gym101177H 

    这是South Pacific Regionals 2016的题目,就是求(xa+q) mod n的余数的种类数(显然q是卖萌的)

    由之前剩余系的基本性质,只要求xa mod piki 的余数种类数,然后求积就可以了

    这要分情况,假如pi是奇素数,那么piki是有原根的,假设为g,我们不妨先考虑与piki互质的x可能的余数

    令gw≡x (mod piki) 由之前原根的性质,这样就是求wa mod φ(piki)可能的余数个数

    显然这个个数就是 φ(piki)/gcd( φ(piki),a)

    而与 piki不互质的x余数的种类数,我们只要枚举x包含pi的几次幂为因数,设x=q*piei

    问题就是转化为xmod piki-a*ei且x与pi互质的情况了

    难点在于考虑p=2的情况,因为除了2,4,p不存在原根,这怎么考虑呢

    还是想刚才一样分x为奇偶考虑(偶数是类似之前处理)

    注意到当a为奇数的时候,所有一共2k-1个奇数都是可能的余数

    证明的话考虑任意两个奇数x1,x2,x1a-x2a=(x1-x2)(x1a-1+x1p-2x2+...+x2p-1)

    前一项是偶数但是一定小于2k,后一项是奇数,因此x1a-x2a模2k不同余,余数自然可以取到2k-1个奇数

    下面考虑a为偶数的情况,显然a可以表示a=q*2e   (q为奇数)

    由之前a为奇数的情况我们知道x相当于把所有奇数做了一个置换,因此我们只要考虑x^(2e) mod 2k可能的余数

    这有点二次剩余的味道,首先很明显x2和(2k-x)2一定是mod 2k同余的

    进一步观察我们会发现x2和(2k-1-x)2也是mod 2k同余的,就是当e=1时,只能取2k-3

    之后e每增加1,也就是多一个平方,就会使两两一组奇数变为同余,所以答案为2k-2-e

    问题得解,撒花

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 int k,q,n;
     5 int quick(int x,int y)
     6 {
     7     int s=1;
     8     while (y)
     9     {
    10         if (y&1) s=s*x;
    11         x=x*x;
    12         y>>=1;
    13     }
    14     return s;
    15 }
    16 
    17 int gcd(int a,int b)
    18 {
    19     return (b==0)?a:gcd(b,a%b);
    20 }
    21 
    22 int calc(int p,int x)
    23 {
    24     if (x<=0) return 1;
    25     if (p>2)
    26     {
    27         int phi=quick(p,x-1)*(p-1);
    28         int s=phi/gcd(phi,k);
    29         s+=calc(p,x-k);
    30         return s;
    31     }
    32     if (x==1) return 2;
    33     int phi=quick(2,x-2);
    34     int s=phi/gcd(phi,k);
    35     if (k%2==1) s*=2;
    36     s+=calc(p,x-k);
    37     return s;
    38 }
    39 
    40 int main()
    41 {
    42     scanf("%d%d%d",&k,&q,&n);
    43     if (k==1)
    44     {
    45         printf("%d
    ",n);
    46         return 0;
    47     }
    48     int ans=1;
    49     for (int i=2; 1ll*i*i<=n; i++)
    50         if (n%i==0)
    51         {
    52             int s=0;
    53             while (n%i==0)
    54             {
    55                 n/=i;
    56                 ++s;
    57             }
    58             ans*=calc(i,s);
    59         }
    60     if (n>1) ans*=calc(n,1);
    61     printf("%d
    ",ans);
    62 }
    gym101177H

    上面那个问题让我想到一个非常有意思的东西叫二次剩余

    就是说x2≡b (mod p)有解,就把b叫作p的二次剩余

    如果我们定义(a/p)=1 (a是p的二次剩余) 或 -1(a不是p的二次剩余)

    那么有一个非常有意思的定理(欧拉判别法)是(a/p)≡a(p-1)/2 (mod p) p是奇素数 a不被p整除

    由此可以退出还有二次剩余乘二次剩余一定是二次剩余,二次剩余乘非二次剩余是非二次剩余,非二次剩余乘非二次剩余是二次剩余

    还有一个著名的二次互反律:设p,q为奇素数

    (q/p)(p/q)=(-1)(p-1)(q-1)/4 就是由q是p的二次剩余如何快速判断p是q的二次剩余

    如果把二次剩余的结论推广到n次,则是这样的(但似乎没有类似高次互反律的东西)

    前提:p是奇质数且p不能整除d,判定xn≡d (mod p)是否有解

    1.若n|(p-1)(即n能整除p-1),则d是模p的n次剩余的充要条件为:d(p-1)/n≡1(mod p)且有解时,解数为n。
    2.若n不能整除p-1,则d是模p的n次剩余的充要条件为:d(p-1)/k≡1(mod p),其中k=(n,p-1),且有解时解数为k。

    感觉关于二次剩余和二次互反律的题目还不多,就先写到这吧

    最后补一个扩展BSGS的模板吧

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 #include<map>
      7 
      8 using namespace std;
      9 typedef long long ll;
     10 int A,p,B;
     11 map<int,int> mp;
     12 
     13 int gcd(int a,int b)
     14 {
     15     return (b==0)?a:gcd(b,a%b);
     16 }
     17 
     18 ll quick(ll x,int y,int mo)
     19 {
     20     ll s=1;
     21     while (y)
     22     {
     23         if (y&1) s=s*x%mo;
     24         x=x*x%mo;
     25         y>>=1;
     26     }
     27     return s;
     28 }
     29 
     30 void exgcd(int a, int b, int &x, int &y)
     31 {
     32      if (!b){x=1; y=0; return;}
     33      else {
     34           exgcd(b,a%b,x,y);
     35           int xx=x, yy=y;
     36           x=yy; y=xx-a/b*yy;
     37      }
     38 }
     39 
     40 int inv(int a,int p)
     41 {
     42     int g=gcd(a,p);
     43     if (g!=1) return -1;
     44     a/=g; int w=p/g,x,k;
     45     exgcd(a,w,x,k); x=(x+w)%w;
     46     return x;
     47 }
     48 
     49 int bsgs(int y,int z,int p)
     50 {
     51     y%=p;
     52     mp.clear();
     53     mp[1]=p-1;
     54     ll m=(int)sqrt(p)+1, now=1;
     55     for (int i=1; i<=m; i++)
     56     {
     57         now=now*(ll)y%p;
     58         if (!mp[now]) mp[now]=i;
     59     }
     60     int x=inv(now,p);
     61     if (x==-1) return -1;
     62     ll step=z;
     63     for (int i=0; i<m; i++)
     64     {
     65         if (mp[step])
     66         {
     67            if (step==1) mp[step]=0;
     68            return mp[step]+i*m;
     69         }
     70         step=1ll*step*x%p;
     71     }
     72     return -1;
     73 }
     74 
     75 int ext(int a,int b,int p)
     76 {
     77     if (b==1) return 0;
     78     ll t=gcd(a,p),d=1,k=0;
     79     while (t!=1)
     80     {
     81         if (b%t) return -1;
     82         ++k,b/=t,p/=t, d=d*(a/t)%p;
     83         if (b==d) return k;
     84         t=gcd(a,p);
     85     }
     86     b=1ll*b*inv(d,p)%p;
     87     int s=bsgs(a,b,p);
     88     if (s==-1) return -1;
     89     else return s+k;
     90 }
     91 
     92 int main()
     93 {
     94     while (scanf("%d%d%d",&A,&p,&B)!=EOF)
     95     {
     96         if (!A&&!p&&!B) break;
     97         int ans=ext(A,B,p);
     98         if (ans==-1) puts("No Solution");
     99         else printf("%d
    ",ans);
    100     }
    101 }
    View Code
  • 相关阅读:
    【容斥】Four-tuples @山东省第九届省赛 F
    【树形dp入门】没有上司的舞会 @洛谷P1352
    【贪心】LIS @The 15th Zhejiang Provincial Collegiate Programming Contest E
    【map离散&容斥】Ghosts @Codeforces Round #478 (Div. 2) D
    PCA & whitening
    Autoencoders and Sparsity(二)
    Autoencoders and Sparsity(一)
    Regularized logistic regression
    Regularization —— linear regression
    Logistic Regression and Newton's Method
  • 原文地址:https://www.cnblogs.com/phile/p/7257991.html
Copyright © 2011-2022 走看看