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;
}


posted @ 2015-10-05 16:14  buzhidaohahaha  阅读(197)  评论(0编辑  收藏  举报