您的位置:首页 > 其它

数学计数-2013 ACM/ICPC Asia Regional Changsha Online-G 题

2013-09-24 01:35 483 查看
题目链接:

http://acm.zju.edu.cn/changsha/showProblem.do?problemCode=G

题目大意:

给一个数x(1<x<=80000),可以用两种运算加法和乘法,问最多用三个质数,凑成x一共有多少种凑法。

解题思路:

数学计数。

先用素数筛选法,筛出素数,总共只有不到8000个,所以用o(n^2)都行。

然后分情况讨论。

1、只有乘法,一个质数相乘,两个质数相乘,三个质数相乘。注意是质数相乘。

2、有乘法有加法。现减去一个质数,然后在判断是否能由两个质数相乘凑。

3、三个质数的加法。分三种情况,都不想等,有两个相同,三个都相同。

4、两个质数的加法。分两种情况,不相等,相等。

先预处理下,两个不同质数组成的和为i的种数,相同的种数。

代码解释的很详细:

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 81000

bool prime[Maxn];
int pp[8000],cnt; //经尝试得一共有7378个
int num1[200000],num2[200000]; //num1[i]表示和为i的且由不同质数组成的种数
//num2[i]表示和为i的且由相同的质数组成的种数
void init()
{
    memset(prime,true,sizeof(prime));
    prime[0]=0;
    prime[1]=0;
    int n=80000;
    for(int i=2;i<=sqrt(n*1.0);i++)
    {
        if(prime[i])
        {
            for(int j=i*2;j<=n;j+=i)
                prime[j]=0;
        }
    }
    cnt=0;
    for(int i=2;i<=n;i++)
        if(prime[i])
            pp[++cnt]=i;
    for(int i=1;i<=cnt;i++) //o(8000^2) 8s差不多了
        for(int j=i+1;j<=cnt;j++)
            num1[pp[i]+pp[j]]++;
    for(int i=1;i<=cnt;i++)
        num2[pp[i]+pp[i]]++;
    //printf("%d\n",cnt);
}
int mul(int n,int num)//求由num个质数相乘是否能得到n
{
    if(prime
) //
    {
        if(num==1)
            return 1;
        else
            return 0;
    }
    int res=0;
    for(int i=1;pp[i]*pp[i]<=n&&i<=cnt;i++)
    {
        while(n%pp[i]==0)
        {
            n/=pp[i];
            res++;
            if(res>num)
                return 0;
        }
    }
    if(n!=1)
        res++;
    if(res==num)
        return 1;
    return 0;

}

int main()
{
   init();
   int x;
   ll ans;

   while(~scanf("%d",&x))
   {
       ans=0;
       ans+=mul(x,1); //一个质数相乘
       ans+=mul(x,2); //两个质数相乘
       ans+=mul(x,3); //三个质数相乘

       //一个加法和一个乘法
       for(int i=1;i<=cnt;i++)
       {
           int t=x-pp[i];
           if(t<4)
                break;
           ans+=mul(t,2);
       }
       //三个数的加法
       //三个数都不相同
       ll temp=0;
       for(int i=1;i<=cnt;i++)
       {
           int t=x-pp[i]; //减去第一个

           if(t>=2)
           {
               if(t-pp[i]>=2&&prime[t-pp[i]])
               {
                   if(x==pp[i]*3) //排除了 和pp[i]相同的情况
                        temp+=num1[t];
                   else
                       temp+=num1[t]-1; //除去一个和pp[i]相等的一种情况

               }
               else
                    temp+=num1[t];
           }
           else
                break;
       }
       ans+=temp/3; //计算了每种情况计算了三次
       //有两个相同,另外一个不相同
       for(int i=1;i<=cnt;i++)
       {
           int t=x-pp[i]*2;
           if(t>=2)
           {
               if(prime[t])
               {
                   if(t!=pp[i])
                        ans++;
               }
           }
           else
                break;
       }
       //三个相同
       for(int i=1;i<=cnt;i++)
       {
           if(x>=pp[i]*3)
           {
               if(x==pp[i]*3)
                    ans++;
           }
           else
                break;
       }
       //两个数加法
       //两个不同数的加法
       ans+=num1[x];
       //两个相同的
       ans+=num2[x];
       printf("%lld\n",ans);

   }
   return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: