题目: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吧,(:在这上面摔过很多次了
来源:https://www.cnblogs.com/celia01/archive/2012/08/03/2621795.html
