ZOJ-3962-数位dp

 
 
 
 
 
 
    16进制下的数位dp,由于固定了位数,可以出现前导零,反而简化了问题,和十进制异曲同工,只需要注意边界,对于[l,r],如果r>max,则分成[l,max-1]+max+[0,r-max-1],这是因为对于cal(N,x)函数来说计算的是[0,N)之间x的出现次数,并不包括N,如果只是简单的把max+1传进去的话就不是八位数了,这
样乱改函数的话更麻烦还不如直接把max抽出来计算方便。
    
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long 
 4 LL c[20]={6,2,5,5,4,5,6,3,
 5           7,6,6,5,4,5,5,4};
 6 LL f[20]={0,1};
 7 LL p16[20]={1};
 8 LL bit[20];
 9 void init(){
10     for(LL i=1;i<=10;++i) p16[i]=p16[i-1]*16;
11     for(LL i=2;i<=10;++i) f[i]=f[i-1]*16+p16[i-1];
12 }
13 LL cal(LL N,LL x){
14     LL len=0,ans=0,tot=0;
15     while(N){
16         bit[len++]=N%16;
17         N/=16;
18     }
19     while(len<8) bit[len++]=0;
20     bit[len]=-1;
21     for(LL i=len-1;i>=0;--i){
22         ans+=f[i]*bit[i];
23         if(bit[i]>x) ans+=p16[i];
24         ans+=tot*bit[i]*p16[i];
25         if(bit[i]==x) tot++;
26     }
27     return ans;
28 }
29 LL to10(char *s){
30     LL ans=0,len=strlen(s);
31     for(int i=0;i<len;++i){
32         LL tmp=isdigit(s[i])?s[i]-'0':(s[i]-'A'+10);
33         ans=ans*16+tmp;
34     }
35     return ans;
36 }
37 int main(){init();
38     char s[15];
39     LL l,r,t,n,i,j,k;
40     LL MAX=to10("FFFFFFFF");
41     scanf("%lld",&t);
42     while(t--){
43         scanf("%lld",&k);
44         scanf("%s",s);
45         LL l=to10(s);
46         LL r=l+k-1;
47         LL ans=0;
48         
49         if(r<=MAX){
50             for(i=0;i<=15;++i) ans+=c[i]*(cal(r+1,i)-cal(l,i));
51         }
52         else{
53             for(i=0;i<=15;++i) if(MAX>=l)ans+=c[i]*(cal(MAX,i)-cal(l,i));
54             ans+=c[15]*8;
55             for(i=0;i<=15;++i) ans+=c[i]*cal(r-MAX,i);
56         }
57         printf("%lld
",ans);
58     }
59     return 0;
60 }
61 /*
62 10
63 5 89ABCDEF
64 3 FFFFFFFF
65 7 00000000
66 */
原文地址:https://www.cnblogs.com/zzqc/p/9009735.html