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 */