[BZOJ]3737 [Pa2013]Euler

从这个FB开始写博客啦。

也不知道会坚持多久……

 

= =似乎要加一句转载请注明出处 http://www.cnblogs.com/DancingOnTheTree/p/4026076.html

 

http://www.lydsy.com/JudgeOnline/problem.php?id=3737

 

因为是好玩的数学题所以刚看见的时候就想捉了……但是无限耽搁这两天才处理掉。

交上去各种瑕疵CE……

改好发现数组开小各种RE……

然后各种TLE……

小看数据了= =。

 

看到题拿各种公式套,似乎是按因数来搜索。咦用2的幂次作深度上界估计似乎能搜?用质数前缀积上界似乎更小?

然后就暴搜吧……

原理是对任意phi(x)=n的x,它的所有素因子-1的积必被n整除。

所以跟先找哪个就没关系了,多了一个下界剪枝。

然后枚举可能的质数的时候只要枚举不超过sqrt(n)的……否则有两种情况。

1、此时x有两个及以上的素因数,那么一定有一个素因数-1是n小于sqrt(n)的因数,不可能找完大于sqrt(n)再回过来找它……

2、x是质数,也就是特判一下n+1是不是质数了。

 

虽然把第二种情况加了Miller_Rabin还是T了……

WTF!太假了!

然后发现能卡我的大多是类似那些高合成数的……总之要判的n+1一般不大。

于是把素数表打大一点,n+1小的时候直接在表里找就过了。

 

最后回过头证明一下搜索能硬上……

设F(k)为前k个质数积,f为其反函数。易知搜索树的宽度深度都是f(n) (n还要变小呢)

而f(n)的话……记得以前推出来是logn/loglogn,错了求不D。

 

然后按传统要羞耻地贴代码喽?……估计不对着代码看也蛮难看懂的。

 

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long LL;
#define rep(i,j,n) for(int i=j;i<=n;i++)
char c;
template < class T> inline void read(T&x){ for (c= getchar ();c< '0' ||c> '9' ;c= getchar ()); for (x=0;c>= '0' &&c<= '9' ;c= getchar ())x=x*10+c- '0' ;};
bool t[4010010];
LL n,p[1000000],ans[1000000];
int T,tot,tott,len,S=10;
void pre(){
     memset (t, true , sizeof (t));
     rep(i,2,4010000) if (t[i]){
         p[++len]=i;
         rep(j,i,4010000/i) t[i*j]= false ;
     }
}
inline LL muti_mod(LL a,LL b,LL c){
     a%=c;
     b%=c;
     LL ret=0;
     while (b){
         if (b&1){ret+=a; if (ret>=c)ret-=c;}
         a<<=1;
         if (a>=c)a-=c;
         b>>=1;
     }
     return ret;
}
inline LL pow_mod(LL x,LL n,LL mod){
     if (n==1) return x%mod;
     int bit[50],k=0;
     while (n){
         bit[k++]=n&1;
         n>>=1;
     }
     LL ret=1;
     for (;k>0;k--){
         ret=muti_mod(ret,ret,mod);
         if (bit[k-1]==1) ret=muti_mod(ret,x,mod);
     }
     return ret;
}
inline bool check(LL a,LL n,LL x,LL t){
     LL ret=pow_mod(a,x,n),last=ret;
     for ( int i=1;i<=t;i++){
         ret=muti_mod(ret,ret,n);
         if (ret==1&&last!=1&&last!=n-1) return true ;
         last=ret;
     }
     if (ret!=1) return true ;
     return false ;
}
inline bool prime(LL n){
     if (n<2) return false ;
     if (n==2) return true ;
     if ((n&1)==0) return false ;
     LL x=n-1;LL t=0;
     while ((x&1)==0){x>>=1;t++;}
     for ( int i=0;i<S;i++)
     {
         LL a= rand ()%(n-1)+1;
         if (check(a,n,x,t)) return false ;
     }
     return true ;
}
void find(LL n, int list,LL now){
     if (n==1){ans[tot++]=now; return ;}
     if (1&n) return ;
     LL N=now,m,maxi= int ( sqrt (n))+1;
     rep(i,list,len) if (p[i]>maxi) break ; else if (n%(p[i]-1)==0){
         m=n/(p[i]-1);
         N*=p[i];
         find(m,i+1,N);
         while (m%p[i]==0){
             m/=p[i];N*=p[i];
             find(m,i+1,N);
         }
         N=now;
     }
     if (n+1>=p[list]){
         if (n+1>p[len]){ if (prime(n+1))ans[tot++]=N*(n+1);}
         else { if (t[n+1])ans[tot++]=N*(n+1);}
     }
}
int main()
{
     read(T);
     pre();
     while (T--){
         read(n);tot=0;
         if (1&n){
             if (n==1){ puts ( "2" ); puts ( "1 2" );} else puts ( "0" ), puts ( "" );
             continue ;
         }
         find(n,1,1);
         sort(ans,ans+tot);
         printf ( "%d\n" ,tot);
         rep(i,0,tot-2) printf ( "%lld " ,ans[i]);
         if (tot) printf ( "%lld\n" ,ans[tot-1]); else { puts ( "" );};
     }
}

 

你可能感兴趣的:(Euler)