您的位置:首页 > 其它

[BZOJ 3884]上帝与集合的正确用法

2017-10-19 19:22 260 查看

Description

给定p,求2222...模p(p≤107)的值,多组询问

传送门

Tag

数论推导,记忆化搜索,线性筛

Solution

这题虽然不难,但是它的处理方式十分经典

我们记所求的那一大坨数字为T,显然有T=2T

不妨设p=2u∗v,其中u≥0且v为奇数

显然有2T≡0(mod2u),于是我们考虑求T模v的值

这时显然有(T,v)=1,于是根据Euler定理,2T≡2r(modv),其中T≡r(modφ(v)),进而问题转化成求解T模φ(v)的值,递归求解即可。

于是我们只需解同余方程

T≡0(mod2u),T≡2r(modv)

由中国剩余定理可以推出,T≡2r+φ(v)(modp)

AC-Code

由于多组询问而且还有递归计算所以我用了记忆化搜索。

事实上不用记忆化也是可以A掉的

时间复杂度分析不来QwQ。。。

如果有哪位dalao会分析麻烦讲下谢谢

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e7+5;

int ans[maxn],phi[maxn],prime[664600],cnt,T,n;
bool vis[maxn];
char c;

inline void read(int& x)
{
x=0,c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=x*10+c-'0',c=getchar();
}
inline LL Pow(LL a,int b,int mod)
{
LL ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod,b>>=1;
}
return ans;
}
inline int solve(int u)
{
if(~ans[u])return ans[u];
int k=0;
while((u&1)==0)u>>=1,k++;   //奇偶因子分离
int res=solve(phi[u]);
res=(res+phi[u]-k%phi[u])%phi[u];
res=Pow(2,res,u);
return ans[u<<k]=(res<<k)%(u<<k);
}

int main()
{
for(int i=2;i<=1e7;i++)
{
if(!vis[i])prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&prime[j]*i<=1e7;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0){phi[prime[j]*i]=phi[i]*prime[j];break;}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}phi[1]=1;
fill(ans+1,ans+(int)1e7+1,-1);
read(T),ans[1]=0;
while(T--)read(n),printf("%d\n",solve(n));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数论 bzoj