想了半天,总算想出来了。这题刚上来的思路很明显是11维DP。。但是明显不可取。。
这题的关键在于只要两个数前面的拥有的数字是一样的,而且此时与其最小公倍数的模是一样的,那么这时候就可以认为对所有的数字取模都是相等的,那么后面的总情况数属于完美数的情况也是相同的。
只要想到这步的话,那么基本思路就出来了,我第一次居然脑残的记录lcm与模2520(2到9的最小公倍数),首先lcm相同并不代表出现的数字相同先不说,仅仅这个最大值就太大了,lcm最大是2520,这样的话就需要开一个20*2520*2520的数组,我一看给的内存挺大的,就这么写了,然后提交。。果然MLE on TEST 1.。。。
然后才用的二进制状压记录2到9出现的数,因为0到1不用记录,所以只需要开1<<8大小就可以了。
这样空间复杂度就少了很多。然后就这样一改,果然AC了。
代码如下:
#include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdio.h> using namespace std; #define LL __int64 #define pi acos(-1.0) const int mod=2520; const int INF=0x3f3f3f3f; const double eqs=1e-8; LL dp[19][257][2530], c[20], lcm[257]; int gcd(int x, int y) { return y==0?x:gcd(y,x%y); } int getlcm(int x, int y) { if(!x) return y; return x*y/gcd(x,y); } int find1(int x, int y) { if(y<=1) return x; y-=2; if(x&(1<<y)) return x; return x|(1<<y); } int init() { int ans=1; int i, j; lcm[0]=1; for(i=1;i<=255;i++){ ans=1; for(j=2;j<=9;j++){ if(i&(1<<(j-2))){ ans=getlcm(ans,j); } } lcm[i]=ans; } } LL dfs(int cnt, int maxd, int zero, int z, int mods) { if(cnt==-1) return !(mods%lcm[z]); if(zero&&maxd&&dp[cnt][z][mods]!=-1) return dp[cnt][z][mods]; int i, r=maxd?9:c[cnt]; LL ans=0; for(i=0;i<=r;i++){ ans+=dfs(cnt-1,maxd||i<r,zero||i,find1(z,i),(mods*10+i)%mod); } if(zero&&maxd) dp[cnt][z][mods]=ans; return ans; } LL Cal(LL x) { int i, cnt=0; while(x){ c[cnt++]=x%10; x/=10; } return dfs(cnt-1,0,0,0,0); } int main() { int t; LL l, r; memset(dp,-1,sizeof(dp)); init(); scanf("%d",&t); while(t--){ scanf("%I64d%I64d",&l,&r); printf("%I64d\n",Cal(r)-Cal(l-1)); } return 0; }
CodeForces 55D Beautiful numbers (树形DP)
原文地址:http://blog.csdn.net/scf0920/article/details/42881393