• 求 A^B mod C. (1<=A,C<=1000000000,1<=B<=10^1000000).(fzu1759,hdu3221,hdu4335)


    题目:http://acm.fzu.edu.cn/problem.php?pid=1759

    也算是快速幂的一题了,只不过这里的指数B特别大。需要用到一个公式:

     A^x = A^(x % Phi(C) + Phi(C)) (mod C),其中x≥Phi(C)

    具体证明可见ac大神博客:http://hi.baidu.com/aekdycoin/item/e493adc9a7c0870bad092fd9。数论学得各种败笔和急于求成,自己的理解就不谈了~直接上代码就是直接用到公式即可:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 using namespace std;
     7 char bb[1000005];
     8 __int64 euler(__int64 x){
     9     __int64 i, res = x;
    10     for(i=2;i<(__int64)sqrt(x*10)+1;i++){//
    11         if(x%i==0){
    12             res = res /i *(i-1);
    13             while(x%i==0) x/=i;
    14         }
    15     }
    16     if(x>1) res = res/x*(x-1);
    17     return res;
    18 }
    19 __int64 quickpow(__int64 m, __int64 n, __int64 k){
    20     __int64 b = 1;
    21     while(n>0){
    22         if(n&1){
    23             b = ((b%k)*(m%k))%k;
    24         }
    25         n = (n>>1);
    26         m = ((m%k)*(m%k))%k;
    27     }
    28     return b;
    29 }
    30 int main(){
    31     __int64 a, c, b, phic, sum;
    32     int i, j, k, l;
    33     while(~scanf("%I64d",&a)){
    34         scanf("%s",bb);
    35         scanf("%I64d",&c);
    36         l = strlen(bb);
    37         phic = euler(c);
    38         if(l<=10){
    39             b = bb[0]-'0';
    40             for(i=1;i<l;i++){
    41                 b = b*10 + (bb[i]-'0');
    42             }
    43             if(b<phic){
    44                 printf("%I64d\n",quickpow(a,b,c));continue;
    45             }
    46         }
    47         b = 0ll;   //求一个很大的数对一个数的模数的方法
    48         for(i=0;i<l;i++){
    49             b = b*10 + (bb[i]-'0');
    50             while(b>=phic){   //while很重要!!!,防溢出
    51                 b -= phic;
    52             }
    53         /*if(b>=phic){
    54         b = b%phic;
    55         }*/
    56         }
    57         printf("%I64d\n",quickpow(a,b+phic,c));
    58     }
    59     return 0;
    60 }

    还有一个09上海赛的题(hdu3221),大致也是那样思路做的,用的矩阵快速幂求的斐波那切数列,套用公式,当天写完之后wa了,过了几天后回来看,发现自己在矩阵快速幂的结构体变量中设的是int而不是long long,模板用的也一直是int,觉得不会超,后来改成long long就A了,结果证明还是超了。推出来的公式是:

    n=1:a

    n=2:b

    n=3:ab

    n=4:ab2

    n=5:a2b3

    n=6:a3b5

    n=7:a5b8

    n=8:a8b13

    正常人应该都能看出规律了吧,就是斐波那切数列,由于指数比较大,要用到指数循环节的公式,中间用矩阵快速幂求斐波那切数列。具体代码:

    View Code
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cmath>
      4 #include<algorithm>
      5 #include<cstring>
      6 #include<cmath>
      7 using namespace std;
      8 __int64 euler(__int64 x){
      9     __int64 i, res = x;
     10     for(i=2;i<(__int64)sqrt(x*10.0)+1;i++){
     11         if(x%i==0){
     12             res = res/i*(i-1);
     13             while(x%i==0) x/=i;
     14         }
     15     }
     16     if(x>1) res = res/x*(x-1);
     17     return res;
     18 }
     19 __int64 quickpow(__int64 m, __int64 n, __int64 k){
     20     __int64 b = 1;
     21     while(n>0){
     22         if(n&1){
     23             b = (b*m)%k;
     24         }
     25         n = (n>>1);
     26         m = (m*m)%k;
     27     }
     28     return b;
     29 }
     30 typedef struct{
     31     __int64 m[2][2];
     32 }Matrix;
     33 Matrix P = {0,1,1,1}, I = {1,0,0,1};
     34 Matrix matrixmul(Matrix a,Matrix b,__int64 p) //矩阵乘法
     35 {
     36        int i,j,k;
     37        Matrix c;
     38        for (i = 0 ; i < 2; i++)
     39            for (j = 0; j < 2;j++)
     40              {
     41                  c.m[i][j] = 0;
     42                  for (k = 0; k < 2; k++){
     43                      c.m[i][j] += (a.m[i][k] * b.m[k][j])%p;   // 取模的值
     44                  }
     45                  c.m[i][j] %= p;
     46              }
     47        return c;
     48 }
     49 
     50 Matrix matrix_exp(Matrix P, Matrix I, __int64 n,__int64 p)
     51 {
     52        Matrix m = P, b = I;
     53        while (n >= 1)
     54        {
     55              if (n & 1)
     56                 b = matrixmul(b,m,p);
     57              n = n >> 1;
     58              m = matrixmul(m,m,p);
     59        }
     60        return b;
     61 }
     62 int main(){
     63     int i, j, k, l, t, T;
     64     __int64 a, b, p, n;
     65     __int64 f1, f2, f3, phip, aa, bb;
     66     scanf("%d",&T);
     67     for(t=1;t<=T;t++){
     68         scanf("%I64d%I64d%I64d%I64d",&a,&b,&p,&n);
     69         phip = euler(p);
     70         printf("Case #%d: ",t);
     71         if(n==1){
     72             printf("%I64d\n",a%p);continue;
     73         }
     74         if(n==2){
     75             printf("%I64d\n",b%p);continue;
     76         }
     77         if(n==3){
     78             printf("%I64d\n",((a%p)*(b%p))%p);continue;
     79         }
     80         f1 = 0; f2 = 1; k = 0;
     81         for(i=4;i<=n;i++){
     82             f3 = f1+f2;
     83             if(f3>=phip){
     84                 f3 %= phip;
     85                 k = 1;break;
     86             }
     87             f1 = f2; f2 = f3;
     88         }
     89         Matrix tmp;
     90         if(k){
     91             tmp = matrix_exp(P,I,n-3,phip);
     92             f1 = tmp.m[1][0]; f2 = tmp.m[1][1];
     93             aa = f2%phip+phip;
     94             f3 = f1+f2;
     95             bb = f3%phip+phip;
     96         }
     97         else{
     98             aa = f3;
     99             f3 = f1+f2;
    100             bb = f3;
    101         }
    102         printf("%I64d\n",(quickpow(a,aa,p)*quickpow(b,bb,p))%p);
    103     }
    104     return 0;
    105 }

    最近的一道题就是前几天多校的题了,还是在那以后才会的这个公式,后来看了题解做的这题,那个trick也不是一般的坑人啊。

    题意:给出三个数 (b, P and M),其中 ( 0<=b<P, 1<=P<=10^5, 1 <= M <=2^64 – 1 ),M的范围即暗示要用unsigned long long了,求满足nn!Ξb (mod p),(0≤n≤M)的n有多少个。也是大整数幂,其中指数很大,所以要用到之前说的指数循环节的公式

    A^x = A^(x % Phi(C) + Phi(C)) (mod C),其中x≥Phi(C)

    对于n!的处理,主要分为一下3部分处理

    1,n很小的时候,直接枚举就可以了,很小指的是n!<phip,此时上述公式也不适用;

    2,公式适用要求x≥phip,此时nn!Ξnn!%phip+phipΞb (mod p),这样可以判断nn!Ξb (mod p)是否成立,但对于每一个n只是套用公式判断是否同余,仍然需要逐个枚举是否满足,而M特别大,枚举所有数必然超时;

    3,可以进一步发现当某个n!%phip==0时,之后的所有n!都能整除phip,上述公式等价于nn!ΞnphipΞb (mod p),指数为固定值,这样就能看出循环节的公式了,根据乘法同余式(shit,这个也看了好久,phip个n相乘)nphipΞ(n%p)phipΞb (mod p),所以在枚举p个n就可以了,当其中某个n成立时,可以知道其后≤M的所有模p同余的数的个数,答案就出来了。(大trick见代码)

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<algorithm>
     6 #define see(x) cout<<#x<<":"<<x<<endl;
     7 using namespace std;
     8 typedef unsigned __int64 LLU;
     9 LLU euler(LLU x){
    10     LLU i, res = x;
    11     for(i=2;i<(LLU)sqrt(x*10.0)+1;i++){
    12         if(x%i==0){
    13             res = res/i*(i-1);
    14             while(x%i==0) x/=i;
    15         }
    16     }
    17     if(x>1) res = res/x*(x-1);
    18     return res;
    19 }
    20 
    21 LLU quickpow(LLU m, LLU n, LLU k){
    22     LLU b = 1;
    23     while(n>0){
    24         if(n&1)
    25             b = (b*m)%k;
    26         n = n>>1;
    27         m = (m*m)%k;
    28     }
    29     return b;
    30 }
    31 LLU f[100001];
    32 int main(){
    33     //freopen("1005.in","r",stdin);
    34     //freopen("out.txt","w",stdout);
    35     int t, T, i , k, l, flag;
    36     LLU b, p, m, phip, ans;
    37     scanf("%d",&T);
    38     for(t=1;t<=T;t++){
    39         ans = 0; flag = 0;
    40         //cin>>b>>p>>m;
    41         scanf("%I64u%I64u%I64u",&b,&p,&m);
    42         if(b==0&&p==1&&m==18446744073709551615ull){
    43             printf("Case #%d: 18446744073709551616\n",t);continue;
    44         }//这里是个大trick,m==18446744073709551615就是2^64-1,如果b==0,p==1,即[0,m]里面所有的数模1都为0,所以一共有2^64个,而2^64已经超过了unsigned long long,所以要特判输出
    45         phip = euler(p);
    46         f[0] = 1;
    47         if(b==0){
    48             ans++;
    49         }
    50         for(i=1;i<=m;i++){
    51             f[i] = f[i-1]*i;
    52             if(f[i]>=phip){
    53                 f[i] = f[i]%phip;
    54                 flag = 1;
    55                 if(f[i]==0){
    56                     break;
    57                 }
    58             }
    59             if(flag){
    60                 if(quickpow(i,f[i]+phip,p)==b){
    61                     ans++;
    62                     
    63                 }
    64             }
    65             else{
    66                 if(quickpow(i,f[i],p)==b){
    67                     ans++;
    68                 }
    69             }
    70         }
    71         for(k=0;i<=m&&k<p;i++,k++){
    72             if(quickpow(i,phip,p)==b){
    73                 ans = ans+1+(m-i)/p;
    74             }
    75         }
    76         printf("Case #%d: %I64u\n",t,ans);
    77         //cout<<ans<<endl;
    78     }
    79     return 0;
    80 }

    利用此题,亲测杭电oj,可以定义long long,只是输入输出不能用%lld,可以用cin,cout,如果用scanf,printf就只能用%I64d,所以要定义__int64。同long long也可以定义unsigned __int64,输入输出可以用%I64u。

    关于数论题目的总结:

    1,数论题目,代码不长,一般知道相关知识,即可求解,但是也要注意细节,以免大意失荆州~~

    2,数学题目以后直接所有变量都设成long long吧,(:在这上面摔过很多次了

     

  • 相关阅读:
    我决定潜心研究技术了...
    new proxy
    谷歌插件开发
    js计算不准确 解决方案
    netty中如何切包
    Spring Boot的ComponentScan原理
    解决org.yaml.snakeyaml.scanner.ScannerException: while scanning for the next token found character '@'
    深入理解js立即执行函数
    winform 将子窗体显示在父窗体的TabControl控件上
    js隐藏网页元素
  • 原文地址:https://www.cnblogs.com/celia01/p/2621795.html
Copyright © 2020-2023  润新知