P4139
上帝与集合的正确用法
这个题还是非常巧妙的。
简单来说,我们设
然后我们根据某个结论可知:
因为这里
那这样,问题转化为了求:
那么同理我们可以转化为:
然后很显然这是个递归求解的问题。
我们知道这样子做的话
然后就是要注意这个 2 的次幂虽然可以用位移来写数值,,但显然当模数过大时这个位数不满足要求,,所以还是老老实实写快速幂罢。。。
关于这个时间复杂度,我们清楚这个指数在每一次递归中都会变得
那么我们可以得到:
那么时间复杂度就大概是
Update: AD20220131
应该是
直接求和计算:
就是等差数列求和,肯定就是
代码:
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const ll N=1e7,M=7e5;
ll T,p,ans,cnt;
bool f[N+5];
ll phi[N+5],prime[M+5];
ll qpow(ll a,ll b,ll p) {
ll res=1;
while(b>0) {
if(b&1) res=(res*a)%p;
a=(a*a)%p;b>>=1;
}
return res%p;
}
ll solve(ll m) {
if(m==1) return 0;
return qpow(2,solve(phi[m])+phi[m],m);
}
void init() {
f[1]=1;phi[1]=1;
for(ll i=2;i<=1e7;i++) {
if(!f[i]) {prime[++cnt]=i;phi[i]=i-1;}
for(ll j=1;j<=cnt&&i*prime[j]<=1e7;j++) {
f[i*prime[j]]=1;
if(i%prime[j]) {
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
else {
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
}
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}
void write(ll x) {
if(x<0) {x=-x;putchar('-');}
ll y=10,len=1;
while(y<=x) {y*=10;len++;}
while(len--) {y/=10;putchar(x/y+48);x%=y;}
}
int main() {
T=read();
init();
while(T--) {
p=read();
ans=solve(p);
write(ans);putchar('\n');
}
return 0;
}