[BZOJ 3884] 上帝与集合的正确用法 欧拉定理

题目传送门:【BZOJ 3884】


题目大意:
多组测试数据。输入整数 p,求

22222...(2)modp

其中1 ≤ p ≤ 107 ,测试数据组数 ≤ 1000。


题目分析:
(一开始看到有无穷多个 2 就懵逼了)
其实这道题没有那么难,因为,你可以试一些数据,比如 p = 2 , 3 , 4 , 5 , 6 , …… 然后发现,随着 2 的幂次方的增长,模下来的数总是一个定值;这之后,我们就要使用数学归纳法(找规律)来得到正确的答案。

欧拉定理:若 gcd ( a , b ) = 1,则 a φ(b)1(modb)

那么对于这道题,它该怎么用呢?显然,对于 22222... ,我们要对它化简,于是我们可以考虑使用这个定理把它的幂转化成较小的数(根据同余的性质)。

举个栗子,gcd ( 4 , 5 ) = 1,则 4φ(5)441(mod5) ;如果我们要求解 499mod5 的值, 那么,这个式子可以转化为: (44)2443mod5 ;前半部分由于同余于 1 直接被消掉,实质上它就变成了求 43mod5 的值了,也可以表示为 499modφ(5)mod5 。这样一来就要简单许多。

同样对于这道题而言,2 的无穷多次方也能转化成这种形式。因为 22222... 只含 2 这个质因子,所以我们可以先把 p 中的 2 提出来,变成 2k(22222...k)modp2k
因为此时 gcd ( 2 , p2k ) = 1,所以可以套用欧拉定理,变成 2k22222...kmodφ(p2k)modp2k

之后,对于 2222...kmodφ(p2k) ,我们可以递归地对它进行处理,直到 p2k = 1 为止(此时模数为 1,意味着任何数模 1 都为 0,于是返回)。然后再加上每次递归时还留下的 kmodφ(p2k) 这个数,回溯回去求出每次取模之后的值,统计答案即可。

本题还有一些需要注意的地方:记得在快速幂时取模!还要开 long long!


下面附上代码:

  1. #include<iostream>  
  2. using namespace std;  
  3. typedef long long LL;  
  4. const int MX=100005;  
  5.   
  6. int T;  
  7. LL pp;  
  8.   
  9. LL getphi(LL x){                            //据说根号 n 的求法更快?   
  10.     LL r=x,tmp=x;  
  11.     for (int i=2;i*i<=tmp;i++){  
  12.         if (tmp%i==0){  
  13.             r=r/i*(i-1);  
  14.             while (tmp%i==0) tmp/=i;  
  15.         }  
  16.     }  
  17.     if (tmp>1) r=r/tmp*(tmp-1);  
  18.     return r;  
  19. }  
  20. LL fastpow(LL a,LL b,LL mod){  
  21.     LL r=1,base=a;  
  22.     while (b){  
  23.         if (b&1) r=r*base%mod;  
  24.         base=base*base%mod;  
  25.         b>>=1;  
  26.     }  
  27.     return r%mod;  
  28. }  
  29. LL solve(LL p,LL k){                        //k:每次取模之后的偏移量(+/-)   
  30.     if (p==1)  
  31.         return 0;  
  32.     LL f2=1,addtime=0;                      //f2:因子2的数量 addtime:2的幂次方   
  33.     while (p%2==0)  
  34.         f2<<=1,p/=2,addtime++;                //求出2的因子数   
  35.     LL phi_p=getphi(p);  
  36.       
  37.     LL temp1=solve(phi_p , ((-addtime%phi_p)+phi_p)%phi_p);  
  38.       
  39.     LL temp2=fastpow(2,temp1,f2*p)%(f2*p)*f2 + k%(f2*p);  
  40.                                             //两个临时变量   
  41.     return temp2%(f2*p);  
  42. }  
  43.   
  44. int main(){  
  45.     cin>>T;  
  46.     while (T–){  
  47.         cin>>pp;  
  48.         cout<<solve(pp,0)<<endl;  
  49.     }  
  50.     return 0;  
  51. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值