题目链接:
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[n]) //
{
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;
}