codevs 4248

我终于做出来了!!!!!(虽然看了题解。。。)

Ps:大神的题解太牛了,我看了一个多小时才看懂。。。

昨天那道题吧,认真想了一会,我就找着了门道,其实就是sum(φ(m)),m为n的约数(1除外)且能被一个完全平方数整除。接着就是枚举,然后求欧拉函数。提交!

我本以为能骗那么个三四十分的,谁知——0分!!!!!所有的点超时!!!

我的天啊,太不人道了!10分都不给我。。。

接着我就试着打个欧拉函数表,优化一下因数分解的方法。然而,并没有什么卵用。。。

于是,我屈服了,打开了大神的题解和代码。

我本以为看了一眼后一切问题就会迎刃而解,可是理想很丰满,现实很骨感。。。

“天啊!”

“这写的都是啥!!!”

“这是什么鬼东西!!!!”

其实我的基本思路是对的,这道题无非就是求上述的那些东西。但是我们的解题方法却相差很远。打个比方吧,我走路,人家坐飞机。我在前面已经将我的步子一一说明了。而至于人家坐的是什么“飞机”呢,接下来就是见证奇迹的时刻:

1.  不直接枚举能被某个完全平方数整除的n的约数,而是枚举既是n的因子又不是任何一个大于1的完全平方数的倍数的数,而本题的答案就是n减去既是n的因子又不是任何一个大于1的完全平方数的倍数的数的欧拉函数的总和。

2. 接下来就是分解质因数,而我也恰恰是在这里卡了好久,因为题解了忽然冒出了一个Pollard rho算法,让我立马就蒙了。然后,我就在网上翻了一个小时的资料,找这个算法。。。至于这个算法是什么,我在此就不做赘述了。以下是对我有较大帮助的几篇文章:

https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm

http://www.cnblogs.com/jackiesteed/articles/2019910.html

http://blog.sina.com.cn/s/blog_86a9d97201015cj7.html

接下来请大家继续欣赏“飞机”。。。

3.     在找质因数的过程中,我们或许会用到质数判断的算法,在我的程序中,我用的是枚举法(我也是醉了。。。)。但是,人家的代码让我想起了一个奇怪的算法。这个算法我曾经用过一次,它miller rabin算法。不过我现在差不多忘光光了,所以我也借此温习了一番。至于这个算法是什么,我在此就不做赘述了。。。

以下是对我有较大帮助的一篇文章:

http://blog.chinaunix.net/uid-26856484-id-3182166.html

 

 

最后附上本题AC代码(官方标程)

 

#include<cstdio>
#include<algorithm>
#define C 2730
#define S 3
using namespace std;
typedef long long ll;
int T,t,i;
ll n,m,q[100];
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll mul(ll a,ll b,lln){return(a*b-(ll)(a/(long double)n*b+1e-3)*n+n)%n;}
inline ll pow(ll a, ll b, ll n)
{
  lld=1;
 a%=n;
 while(b){
   if(b&1)d=mul(d,a,n);
   a=mul(a,a,n);
   b>>=1;
  }
 return d;
}
inline bool check(ll a,ll n){
  llm=n-1,x,y;int i,j=0;
 while(!(m&1))m>>=1,j++;
 x=pow(a,m,n);
 for(i=1;i<=j;x=y,i++){
   y=pow(x,2,n);
   if((y==1)&&(x!=1)&&(x!=n-1))return 1;
  }
 return y!=1;
}
inline bool miller_rabin(int times,ll n){
 if(n==1)return 0;
 if(n==2)return 1;
 if(!(n&1))return 0;
 while(times--)if(check(rand()%(n-1)+1,n))return 0;
 return 1;
}
inline ll pollard_rho(ll n,int c){
  lli=1,k=2,x=rand()%n,y=x,d;
 while(1){
   i++,x=(mul(x,x,n)+c)%n,d=gcd(y-x,n);
   if(d>1&&d<n)return d;
   if(y==x)return n;
   if(i==k)y=x,k<<=1;
  }
}
void findfac(ll n,int c){
 if(n==1)return;
 if(miller_rabin(S,n)){
   q[++t]=n;
   return;
  }
  llm=n;
 while(m==n)m=pollard_rho(n,c--);
 findfac(m,c),findfac(n/m,c);
}
int main()
{
 scanf("%d",&T);
 while(T--){
   scanf("%lld",&n);
   t=0;
   findfac(n,C);
   sort(q+1,q+t+1);
   for(i=m=1;i<=t;i++)if(q[i]!=q[i-1])m*=q[i];
   printf("%lld\n",n-m);
  }
 return 0;
}
 
  llm=n;
 while(m==n)m=pollard_rho(n,c--);
 findfac(m,c),findfac(n/m,c);
}
int main()
{
 scanf("%d",&T);
 while(T--){
   scanf("%lld",&n);
   t=0;
   findfac(n,C);
   sort(q+1,q+t+1);
   for(i=m=1;i<=t;i++)if(q[i]!=q[i-1])m*=q[i];
   printf("%lld\n",n-m);
  }
 return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值