BZOJ3884: 上帝与集合的正确用法

好神的一道欧拉函数。。
根据欧拉定理讲Mod展开为 2k 与j
这里 Mod=2k+j 然后加上欧拉定理就可以了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;

char c;
inline void read(int &a)
{
   a=0;do c=getchar();while(c<'0'||c>'9');
  while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();
}
inline
int Pow(int A,int Pow,int Mod)
{
   int res=1,Cur=A,j=1;
     while(Pow)
    {
        if(j&Pow)res=Cur*1ll*res%Mod,Pow^=j;
        Cur=Cur*1ll*Cur%Mod;j<<=1;
     }
  return res;
}

inline
int Calck(int C)
{
   int l=C&-C;
   if(l==1)return 0;
   if(l==2)return 1;
   if(l==4)return 2;
   if(l==8)return 3;
   if(l==16)return 4;
   if(l==32)return 5;
   if(l==64)return 6;
   if(l==128)return 7;
   l/=128;   
   if(l==2)return 8;
   if(l==4)return 9;
   if(l==8)return 10;
   if(l==16)return 11;
   if(l==32)return 12;
   if(l==64)return 13;
   if(l==128)return 14;
   l/=128;   
   if(l==2)return 15;
   if(l==4)return 16;
   if(l==8)return 17;
   if(l==16)return 18;
   if(l==32)return 19;
   if(l==64)return 20;
   if(l==128)return 21;
   l/=128;   
   if(l==2)return 22;
   if(l==4)return 23;
   if(l==8)return 24;
   if(l==16)return 25;
   if(l==32)return 26;
   if(l==64)return 27;
   if(l==128)return 28;
   l/=128;   
   if(l==2)return 29;
   if(l==4)return 30;
   if(l==8)return 31;
   if(l==16)return 32;   
}
const
  int Maxn=10001;
int Fai[Maxn];
int prime[Maxn];
bool Check[Maxn];

int F(int x)
{
   if(x<Maxn)return Fai[x];
   int res=1,t=sqrt(x),i;
   for(i=1;prime[i]<=t;i++)
   if(x%prime[i]==0)
     {
        res*=prime[i]-1;
        x/=prime[i]; 
        while(x%prime[i]==0)x/=prime[i],res*=prime[i];
        if(x==1)break;
     }
  if(x!=1)
   res*=x-1;  
  return res;
}


const 
 int INF=1<<29;
int DFS(int Mod)
{
  if(Mod==1)return 0;    
  int k=Calck(Mod),j=Mod>>k,t=F(j);
  int l=DFS(t); 
  l-=k;
  l%=t;l+=t;l%=t;
  l=Pow(2,l,j);
  l%=j;
  l*=Pow(2,k,INF);
  return l;
}
int tot;
int main()
{
   int i,j,k;
  Fai[1]=1; 
  for(i=2;i<Maxn;i++) 
   {
      if(!Check[i])prime[++tot]=i,Fai[i]=i-1;
      for(j=1;j<=tot;j++)
        {
          k=prime[j]*i;
          if(k>=Maxn)break;
          Check[k]=true; 
          if(i%prime[j]==0){Fai[k]=prime[j]*Fai[i];break;} 
          Fai[k]=prime[j]*Fai[i]-Fai[i];
        }
   }
   int T,p;  
  read(T);
  while(T--) 
  {
     read(p);
    int Ans=DFS(p); 
    printf("%d\n",Ans);
  }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值