hdu 3271 数位统计 的 DP
要解决的核心问题是:求1~x(在base进制下)的区间内各数位上的数字之和恰好为m的数的个数
对于每一个询问,先预处理一个DP
dp[i][j]表示在base进制下 长度为i且 数位之和为j的数的个数
然后在calc()中一位一位统计过去就好了
View Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 lld; lld dp[32][331];//dp[i][j]:长度为i,数位和为j时的数的个数 void init(int lim,int base){//在base进制下 memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i<32;i++) for(int j=0;j<=lim;j++) for(int k=0;k<base&&k<=j;k++) dp[i][j]+=dp[i-1][j-k]; } lld calc(int base,int x,int lim){ lld ret=0,pre=0; int bit[32],tot=0;; while(x){ bit[++tot]=x%base;; x/=base; } for(int i=tot;i>=1;i--){ for(int j=0;j<bit[i]&&lim-pre-j>=0;j++){//逐位统计过去 ret+=dp[i-1][lim-pre-j]; } pre+=bit[i]; } if(pre==lim) ret++; return ret; } int main(){ int q,i,j,k,m,x,y,b,cases=1; while(scanf("%d",&q)!=EOF){ scanf("%d%d%d%d",&x,&y,&b,&m); init(m,b); if(x>y) swap(x,y); if(q==1) { printf("Case %d:\n%I64d\n",cases++,calc(b,y,m)-calc(b,x-1,m)); } else { scanf("%d",&k); lld l=x,r=y,mid; lld tmp=calc(b,x-1,m); lld ans=-1; while(l<=r){ mid=(l+r)>>1; if(calc(b,mid,m)-tmp>=k){ ans=mid; r=mid-1; } else l=mid+1; } printf("Case %d:\n",cases++); if(ans==-1) printf("Could not find the Number!\n"); else printf("%I64d\n",ans); } } }