【BZOJ】1799: [Ahoi2009]self 同类分布
【题意】给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。1 ≤ a ≤ b ≤ 10^18
【算法】数位DP
【题解】
感觉这种方法很暴力啊。
枚举数位和1~162(不能枚举0,不然会模0,相当于除0),记忆化f[pos][sum][val],sum表示当前数位和,val表示数字取模枚举的数位和。
每次sum+i和(val*10+i)%MOD转移。
sum用减法优化,即记忆化(MOD-sum),但是枚举过程中都要memset,导致效率低下,记忆化效果很差。
要什么方法才能跑1.3s啊,%%%。
#include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int maxn=20; ll f[maxn][200][200],a[maxn],b[maxn],c[maxn],n,MOD; ll dfs(int pos,int sum,int val,int limit){ if(sum>MOD)return 0; if(pos==-1){if(sum==MOD&&val==0)return 1;else return 0;} if(!limit&&~f[pos][MOD-sum][val])return f[pos][MOD-sum][val]; int up=limit?a[pos]:9; ll ans=0; for(int i=0;i<=up;i++){ ans+=dfs(pos-1,sum+i,(val*10+i)%MOD,limit&&i==up); } if(!limit)f[pos][MOD-sum][val]=ans; return ans; } int main(){ ll A,B,cntb=0,cntc=0; scanf("%lld%lld",&A,&B); A--; while(A){ b[cntb++]=A%10; A/=10; } while(B){ c[cntc++]=B%10; B/=10; } ll ans=0; for(int i=1;i<=162;i++){//不能模0啊!!!模0也是除0啊!!! memset(f,-1,sizeof(f)); MOD=i; n=cntb; for(int j=0;j<n;j++)a[j]=b[j]; ans-=dfs(n-1,0,0,1); n=cntc; for(int j=0;j<n;j++)a[j]=c[j]; ans+=dfs(n-1,0,0,1); } printf("%lld",ans); return 0; }