BZOJ3884上帝与集合的正确用法-欧拉函数

mac2026-08-06  12

刚开始我想的是欧拉降幂,可是觉得复杂度还是挺高的就去找了一下题解。

思路大方向没有问题,仍然是使用欧拉函数降低指数然后递归处理。但是不是简单的使用欧拉降幂而是应该对模数p稍微处理一下。因为底数已经确定为2,所以我们可以将p写成p=2k*q,q为奇数的形式,则

22的无穷次方%p=2k(22的无穷次方-k%q)

这样可以直接对新的指数直接模q的欧拉函数值进行处理,因为每次q为奇数,q的欧拉函数值一定为偶数(他的因子一定是奇素数,奇素数的欧拉函数值一定是偶数),所以每次至少将p变为原来的一半,所以复杂度比较低。因为用到的欧拉函数值不是很多,所以直接求单点的欧拉函数值即可。

需要注意的是指数-k模欧拉函数值时应该保证指数大于0

AC代码

#include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cmath> #include<cstdlib> #include<ctime> #include<map> #include<queue> #include<set> #include<vector> using namespace std; typedef long long ll; const int MAXN=1e5+5; ll p; ll quick_pow(ll a,ll b,ll p) { a%=p; ll ret=1; while(b) { if(b&1) ret=ret*a%p; a=a*a%p; b>>=1; } return ret; } ll quick_pow(ll a,ll b) { ll ret=1; while(b) { if(b&1) ret*=a; a*=a; b>>=1; } return ret; } ll phi(int x) { ll ret=x; for(int i=2;i*i<=x;i++) { if(x%i==0) { ret=ret-ret/i; while(x%i==0) x/=i; } if(x==1) break; } if(x>1) ret=ret-ret/x; return ret; } ll deal(ll p) { ll k=0; while(~p&1) k++,p>>=1; if(p==1) return 0; ll phip=phi(p); return quick_pow(2,(deal(phip)-k%phip+phip)%phip,p)<<k; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%lld",&p); printf("%lld\n",deal(p)); } return 0; }
最新回复(0)