题意: 只允许使用乘法、加法(不考虑括号)和最多三个素数,问用多少种方法得到整数X?
分析: 易知共有以下6种情况:
1. a;
2. a*b;
3. a*b*c;
4. a*b+c;
5. a+b;
6. a+b+c;
对于前四种(或五种)情况可以直接暴力预处理出来,而第六种和值增长太慢,不能这么处理。怎么办呢?
可以这样做:先处理出两个素数和的方法数,再枚举第三个素数。相信很多人考虑过这个方法,可是还存在一个小问题,这样做会有重复情况,如10=(2+3)+5 = (2+5)+3,实际是同一种情况,但计算了两次。仔细一想,不就是重复了, 除以重复次数不就完了吗?
还需要注意一下:对于a==b==c 需要特殊处理一下。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
const int maxn=80010;
const LL MOD=1000000007;
const int NN=7837;
LL cnt[maxn],CNT[maxn];
bool flag[maxn+1];
int prime[maxn/4];
int calc_prime()
{
int m=(int)sqrt(maxn+0.5);
int cnt=0;
int i;
flag[0]=flag[1]=true;
for(i=2;i<=m;i++) if(!flag[i])
{
prime[cnt++]=i;
for(int j=i*i;j<=maxn;j+=i) flag[j]=true;
}
for(;i<=maxn;i++) if(!flag[i])
prime[cnt++]=i;
return cnt;
}
void PreWork()
{
calc_prime();
for(int i=0;i<NN;i++){
for(int j=i;j<NN;j++){
for(int k=j;k<NN;k++){
LL tmp=(LL)prime[i]*prime[j]*prime[k];
if(tmp>=maxn) break;
cnt[tmp]++;
}
LL tmp=(LL)prime[i]*prime[j];
if(tmp>=maxn) break;
cnt[tmp]++;
}
cnt[prime[i]]++;
}
for(int i=0;i<NN;i++){
for(int j=i;j<NN;j++){
if(prime[i]*prime[j]>=maxn) break;
for(int k=0;k<NN;k++){
LL tmp=(LL)prime[i]*prime[j]+prime[k];
if(tmp>=maxn) break;
cnt[tmp]++;
}
}
}
for(int i=0;i<NN;i++){
for(int j=0;j<NN;j++){
LL tmp=prime[i]+prime[j];
if(tmp>=maxn) break;
CNT[tmp]++;
}
}
}
int main()
{
//freopen("out.txt","w",stdout);
//freopen("in.txt","r",stdin);
PreWork();
LL x;
while(scanf("%lld",&x)!=EOF)
{
LL ans=0;
for(int i=0;prime[i]<x;i++){
ans+=CNT[x-prime[i]];
if(x>prime[i]*2 && !flag[x-prime[i]*2])
ans+=3;
}
if(x%3==0 && !flag[x/3]) ans+=5;
ans/=6;
LL tt=CNT[x];
if(x%2==0 && !flag[x/2]) tt++;
tt/=2;
ans=(ans+tt+cnt[x])%MOD;
printf("%lld\n",ans);
}
return 0;
}