51nod1230幸运数

来源:互联网 发布:10月中国经济数据 编辑:程序博客网 时间:2024/05/22 10:24

51nod1230幸运数

Description
如果一个数各个数位上的数字之和是质数,并且各个数位上的数字的平方和也是质数,则称它为幸运数。
例如:120是幸运数,因为120的数字之和为3,平方和为5,均为质数,所以120是一个幸运数字。
给定x,y,求x,y之间( 包含x,y,即闭区间[x,y])有多少个幸运数。

Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数,X, Y中间用空格分割。(1 <= X <= Y <= 10^18)

Output
输出共T行,对应区间中幸运数的数量。

Sample Input

21 20120 130

Sample Output

41

解题思路

10000询问,然后10^18大的数,加上数位之和,平方和是不是质数,一看就是数位DP

先想想T=1的
f[i][j][k][0/1]表示完成到第i位,数之和为j,平方和为k,1~i的数字是否与上界相同,的方案数,很好推嘛,也就189188118=4251528

T很大很大时呢?O(41010)
那么,换一种设法
f[i][j][k]表示当前数字和为j,平方和为k,后i位任选并符合题目要求的方案数,也挺好推的

但是上界呢?
由于想到被上界限制的只有18个,并且只会多引申出180个询问,考虑用dfs算每一个dp值,更新的次数也只有4251528这么多

codes:

#include<cstring>#include<cstdio>#define ll unsigned long longusing namespace std;int let[20],n;ll pr[2000],f[19][163][1459];bool prime[2000];ll dp(int w,int sum,int sum2,bool flag){    if(w==n)return (prime[sum]|prime[sum2])^1;    if(!flag && f[n-w][sum][sum2]!=-1)return f[n-w][sum][sum2];    if(flag){        ll ret=0;int i=0;        for(i=0;i<let[n-w];i++)ret+=dp(w+1,sum+i,sum2+i*i,0);        ret+=dp(w+1,sum+i,sum2+i*i,1);return ret;    }    ll ret=0;    for(int i=0;i<=9;i++)ret+=dp(w+1,sum+i,sum2+i*i,0);    return (f[n-w][sum][sum2]=ret);}ll q(ll x){    if(!x)return 0;    for(n=0;x;x/=10)let[++n]=x%10;    return dp(0,0,0,1);}int main(){    prime[0]=prime[1]=1;    memset(f,255,sizeof(f));    for(int i=1;i<2000;i++){        if(!prime[i])pr[++pr[0]]=i;        for(int j=1;j<=pr[0] && i*pr[j]<2000;j++){            prime[i*pr[j]]=1;if(i%pr[j]==0)break;        }    }    int t;scanf("%d",&t);    while(t--){        ll l,r;scanf("%llu %llu",&l,&r);        printf("%llu\n",q(r)-q(l-1));    }}

其实有时候想到数位DP,但是有多组数据,可以考虑放宽限制(取消上界限制),再用dfs求答案。