bzoj3629
参考链接:
http://baike.baidu.com/view/9970469.htm
http://blog.csdn.net/popoqqq/article/details/39152381
http://blog.csdn.net/eolv99/article/details/39644419
约数和定理:
对于一个大于1正整数n可以分解质因数:n=p1^a1*p2^a2*p3^a3*…*pk^ak,则由约数个数定理可知n的正约数有(a₁+1)(a₂+1)(a₃+1)…(ak+1)个,那么n的(a₁+1)(a₂+1)(a₃+1)…(ak+1)个正约数的和为f(n)=(p1^0+p1^1+p1^2+…p1^a1)(p2^0+p2^1+p2^2+…p2^a2)…(pk^0+pk^1+pk^2+…pk^ak)
如果已经筛出1到N的素数后,那么再判断某个数是不是素数时,如果小于等于N,则可以直接根据前面筛的结果判断,如果大于N的话,那么就用小于等于它的开平方数的素数去除它。(代码中算出的是1到N-1的素数),这种方法可以应用于需要求1到M(M是一个很大很大的数)的素数时,如果全部筛的话,空间可能不够,但是每个都遍历2到开平方的话,就会超时,此时就可以用这种方法,当然,其他时候,只要这种方法有用,就都可以用。
ac的代码:
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define N 100000//到底数据中n的范围是多少?肯定不是2*109,不然我的纯暴力不会超时。 #define M 100010 bool isprime[N]; int prime[N]; int cou; long long int ans[M]; int anscou; void getprime(){//已验证,正确 cou=0; memset(isprime,true,sizeof(isprime)); isprime[0]=false; isprime[1]=false; for(int i=2;i<N;i++){ if(isprime[i]){ prime[cou++]=i; } for(int j=0;prime[j]*i<N&&j<cou;j++){ isprime[i*prime[j]]=false; if(i%prime[j]==0){ break; } } } return; } bool fisprime(long long int x){//已验证,正确 if(x==1){ return false; } else if(x<N){//可以利用已经计算过的,就不用再麻烦了 return isprime[x]; } /*for(int i=0;prime[i]*prime[i]<=x;i++){//利用所有已经算出的有用的信息,原先是遍历2到sqrt(x)找x的素因数 if(x%prime[i]==0){ return false; } }*/ for(int i=2;i*i<=x;i++){ if(x%i==0){ return false; } } return true; } void dfs(int start,long long int now,long long int n){//已验证,正确 //printf("%lld %lld\n",now,n); //(1)和(2)并不矛盾,所以不是else if的关系,并且不会重复,因为 n-1 是质数,并且 (n-1)^2<n 的数不存在。当s是合数的约数和是,从(2)出发可以找到找那个合数,当s是素数的约数和时,从(1)出发可以找到那个素数,并且无法从(2)出发找到,所以(1)和(2)不重复,不遗漏。 if(n==1){ ans[anscou++]=now; //return; } else if(fisprime(n-1)&&n-1>=prime[start]){//(1) ans[anscou++]=now*(n-1); } for(int i=start;i<cou&&prime[i]*prime[i]<n;i++){//(2) 这里的prime[i]*prime[i]<n也可以,当然<=也可以 long long int sum=1+prime[i]; for(long long int j=prime[i];sum<=n;j=prime[i]*j,sum=sum+j){ if(n%sum==0){ dfs(i+1,now*j,n/sum); } } } return; } int main(){ getprime(); long long int n; while(scanf("%lld",&n)!=EOF){//试出来一组数据是n=9950 /*if(n==9950){ printf("%d\n",1/0); }*/ anscou=0; dfs(0,1,n); sort(ans,ans+anscou); bool pd=false; printf("%d\n",anscou);//把应该输出的,注释掉了 for(int i=0;i<anscou;i++){ if(pd!=false){ printf(" "); } else{ pd=true; } printf("%lld",ans[i]); } if(anscou!=0){ printf("\n"); } } return 0; }
超时:
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define N 100000//到底n的范围是多少?<span style="color: rgb(51, 51, 51); font-size: 14px; line-height: 24px; text-indent: 28px; font-family: Arial, Helvetica, sans-serif;">不然我的纯暴力不会超时</span> #define M 100010 bool isprime[N]; int prime[N]; int cou; long long int ans[M]; int anscou; void getprime(){//已验证,正确 cou=0; memset(isprime,true,sizeof(isprime)); isprime[0]=false; isprime[1]=false; for(int i=2;i<N;i++){ if(isprime[i]){ prime[cou++]=i; } for(int j=0;prime[j]*i<N&&j<cou;j++){ isprime[i*prime[j]]=false; if(i%prime[j]==0){ break; } } } return; } bool fisprime(long long int x){//已验证,正确 if(x==1){ return false; } else if(x<N){//可以利用已经计算过的,就不用再麻烦了 return isprime[x]; } /*for(int i=0;prime[i]*prime[i]<=x;i++){//利用所有已经算出的有用的信息,原先是遍历2到sqrt(x)找x的素因数 if(x%prime[i]==0){ return false; } }*/ for(int i=2;i*i<=x;i++){ if(x%i==0){ return false; } } return true; } void dfs(int start,long long int now,long long int n){//已验证,正确 //printf("%lld %lld\n",now,n); //(1)和(2)并不矛盾,所以不是else if的关系 if(n==1){ ans[anscou++]=now; //return; } else if(fisprime(n-1)&&n-1>=prime[start]){//(1) ans[anscou++]=now*(n-1); } for(int i=start;i<cou&&prime[i]*prime[i]<n;i++){//(2) 这里的prime[i]*prime[i]<n也可以,当然<=也可以,因为这里所以确定上面个要线性筛的素数的范围,所以要根据n的最大值来确定 long long int sum=1+prime[i]; for(/*long long */int j=prime[i];sum<=n;j=prime[i]*j,sum=sum+j){//这里把j改成long long int就ac,改成int就超时,为什么呢? if(n%sum==0){ dfs(i+1,now*j,n/sum); } } } return; } int main(){ getprime(); long long int n; while(scanf("%lld",&n)!=EOF){//试出来一组数据是n=9950 /*if(n==9950){ printf("%d\n",1/0); }*/ anscou=0; dfs(0,1,n); sort(ans,ans+anscou); bool pd=false; printf("%d\n",anscou);//把应该输出的,注释掉了 for(int i=0;i<anscou;i++){ if(pd!=false){ printf(" "); } else{ pd=true; } printf("%lld",ans[i]); } if(anscou!=0){ printf("\n"); } } return 0; }
整理后又从写的一遍,已ac:
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define N 100010 bool isprime[N]; int primenum[N]; int cou; long long int ans[N]; int anscou; void getprime(){ cou=0; memset(isprime,true,sizeof(isprime)); isprime[0]=false; isprime[1]=false; for(int i=2;i<N;i++){ if(isprime[i]){ primenum[cou++]=i; } for(int j=0;primenum[j]*i<N&&j<cou;j++){ isprime[primenum[j]*i]=false; if(i%primenum[j]==0){ break; } } } return; } bool fisprime(long long int x){ if(x<=1){ return false; } else if(x<N){ return isprime[x]; } else{ for(int i=0;i<cou&&primenum[i]*primenum[i]<=x;i++){ if(x%primenum[i]==0){ return false; } } return true; } } void dfs(int start,long long int now,long long int n){ if(n==1){ ans[anscou++]=now; return; } else{ if(fisprime(n-1)&&n-1>=primenum[start]){ ans[anscou++]=now*(n-1); } for(int i=start;i<cou&&primenum[i]*primenum[i]<n;i++){ long long int sum=1+primenum[i]; for(long long int j=primenum[i];sum<=n;j=j*primenum[i],sum=sum+j){ if(n%sum==0){ dfs(i+1,now*j,n/sum); } } } } return; } int main(){ getprime(); long long int n; while(scanf("%lld",&n)!=EOF){ anscou=0; dfs(0,1,n); sort(ans,ans+anscou); printf("%d\n",anscou); for(int i=0;i<anscou;i++){ if(i!=0){ printf(" "); } printf("%lld",ans[i]); } if(anscou!=0){ printf("\n"); } } return 0; }