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);
        }
    }
}

 

posted @ 2012-05-15 13:44  Because Of You  Views(339)  Comments(0Edit  收藏  举报