题目链接:
https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370089
题目大意:
给你一个16进制数s和偏移量n(注意超出的话就从00000000重新开始),0-F分别对应一种权值,求s到s往后加n位的所有数的每一位的权值和,包含前导0.
题解:
首先要清楚利用数位dp我们可以直接求出L到R的所有数的每一位的权值和或者求出L到R中所有数的每一位的出现次数,然后乘以对应权值相加就是答案。但是这里有一种情况需要考虑,就是当s往后移n位大于FFFFFFFF了,这时就需要分成两个区间[s,FFFFFFFF]和[00000000,s+n]来处理,然后套用第一种情况的解法就行了。
trick:注意memset要在T的外面,这样才不会超时。。。。
代码实现:
解法一:
数位dp计算出现次数再乘以权值的解法
#pragma GCC optimize(2) #include <iostream> #include <algorithm> #include <cmath> #include <cstring> #include <cstdio> #include <cstdlib> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #define PI atan(1.0) * 4 #define E 2.718281828 #define rp(i, s, t) for (register int i = (s); i <= (t); i++) #define RP(i, t, s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a, b) memset(a, b, sizeof(a)) #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 #define pii pair<int, int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a = 0, b = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') b = -1; c = getchar(); } while (c >= '0' && c <= '9') { a = (a << 3) + (a << 1) + c - '0'; c = getchar(); } return a * b; } ll val[20]={6,2,5,5,4,5,6,3,7,6,6,5,4,5,5,4}; int tran[305]; char r[20]; char l[20]; char sta[15]; void init() { for(char i = '0'; i <= '9'; ++i) tran[(int)i] = i - '0'; for(char i = 'A'; i <= 'F'; ++i) tran[(int)i] = i - 'A' + 10; for(int i = 0; i <= 9; ++i) sta[i] = i + '0'; for(int i = 10; i <= 15; ++i) sta[i] = i + 'A' - 10; } void getstr(int n) { ll clk = n - 1; for(int i = 7; i >= 0; --i) { ll num_zq = clk / 16; ll remn = clk % 16; r[i] = sta[(tran[l[i]] + remn) % 16]; ll jw = num_zq; if(remn >= 15 - tran[l[i]] + 1) ++jw; clk = jw; } r[8] = 0; } ll dp[20][20]; ll num; char curNum; char x[20]; void strRev(char s[]){ char tt[10]; strcpy(tt,s); for(int i=0;i<=7;i++) s[i]=tt[7-i]; } ll dfs(int pos,int sum,int limit){ if(pos==-1){ return sum; } if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum]; char up=limit?x[pos]:'F'; ll ans=0; int Up=(up>='A'&&up<='F'?up-'A'+10:up-'0'); // cout<<up<<" "<<Up<<endl; rp(i,0,Up){ char tt; if(i<=9) tt=i+'0'; else tt=i-10+'A'; ans+=dfs(pos-1,sum+(tt==curNum),limit&&tt==x[pos]); } if(!limit) return dp[pos][sum]=ans; return ans; } ll solve(char t[]){ strcpy(x,t); strRev(x); // cout<<x<<endl; return dfs(7,0,1); } int main(){ // curNum='1'; // char s[]="00000001"; // cout<<solve(s)<<endl; int T;scanf("%d",&T); init(); ll delta=0; strcpy(r,"FFFFFFFF"); strcpy(l,"00000000"); rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); delta+=(solve(r)-solve(l))*val[i]; // printf("%d\n",) } delta+=48; mst(dp,-1); ll tt=1; for(int i=1;i<=8;i++) tt*=16; while(T--){ int n; scanf("%d%s",&n,l); getstr(n); ll num=0; ll ts=1; for(int i=7;i>=0;i--){ num+=(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')*ts; ts*=16; } // cout<<num<<endl; // printf("%s\n",r); ll ans=n/tt*delta; // cout<<n/tt<<endl; if(n/tt==0){ if(num+n%tt>tt){ // cout<<"Ac"<<endl; char RR[20]; strcpy(RR,r); strcpy(r,"FFFFFFFF"); rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); ans+=(solve(r)-solve(l))*val[i]; // printf("%d\n",) } for(int i=0;i<=7;i++) ans+=val[(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')]; strcpy(l,"00000000"); rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); ans+=(solve(RR)-solve(l))*val[i]; // printf("%d\n",) } for(int i=0;i<=7;i++) ans+=val[(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')]; printf("%lld\n",ans); } else{ rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); ans+=(solve(r)-solve(l))*val[i]; } for(int i=0;i<=7;i++) ans+=val[(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')]; printf("%lld\n",ans); } } else{ char RR[20]; strcpy(RR,r); strcpy(r,"FFFFFFFF"); rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); ans+=(solve(r)-solve(l))*val[i]; // printf("%d\n",) } for(int i=0;i<=7;i++) ans+=val[(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')]; strcpy(l,"00000000"); rp(i,0,15){ if(i<=9) curNum=i+'0'; else curNum=i-10+'A'; // printf("%lld\n",solve(r)-solve(l)); ans+=(solve(RR)-solve(l))*val[i]; // printf("%d\n",) } for(int i=0;i<=7;i++) ans+=val[(l[i]>='A'&&l[i]<='F'?l[i]-'A'+10:l[i]-'0')]; printf("%lld\n",ans); } } return 0; } /* 3 5 89ABCDEF 3 FFFFFFFF 7 00000000 208 124 327 1 3 FFFFFFFF 124 2 10 ABCD00FF 17 00000000 389 794 */
解法二:
数位dp直接计算权值的解法
#pragma GCC optimize(2) #include <iostream> #include <algorithm> #include <cmath> #include <cstring> #include <cstdio> #include <cstdlib> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #define PI atan(1.0) * 4 #define E 2.718281828 #define rp(i, s, t) for (register int i = (s); i <= (t); i++) #define RP(i, t, s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a, b) memset(a, b, sizeof(a)) #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 #define pii pair<int, int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a = 0, b = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') b = -1; c = getchar(); } while (c >= '0' && c <= '9') { a = (a << 3) + (a << 1) + c - '0'; c = getchar(); } return a * b; } ll val[20]={6,2,5,5,4,5,6,3,7,6,6,5,4,5,5,4}; char l[20]; ll dp[20][200]; int a[20]; int get(char x){ if(x>='A') return x-'A'+10; else return x-'0'; } ll dfs(int pos,ll sum,int limit){ if(pos==-1) return sum; if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum]; ll ans=0; int up=limit?a[pos]:15; // cout<<up<<" "<<Up<<endl; rp(i,0,up) ans+=dfs(pos-1,sum+val[i],limit&&i==a[pos]); if(!limit) return dp[pos][sum]=ans; return ans; } ll solve(ll x){ if(x==-1) return 0; int len=0; memset(a,0,sizeof(a)); while(x) a[len++]=x%16,x/=16; return dfs(7,0,1); } int main(){ // cout<<solve(1)<<endl; int T;scanf("%d",&T); mst(dp,-1); while(T--){ ll n; scanf("%lld%s",&n,l); ll L=0; ll lim=0; for(int i=0;i<=7;i++){ L=L*16+get(l[i]); lim=lim*16+get('F'); } // cout<<tt<<" "<<L<<endl; ll R=L+n-1; ll ans=0; if(R<=lim) ans+=solve(R)-solve(L-1); else ans+=solve(R-lim-1)+solve(lim)-solve(L-1); printf("%lld\n",ans); } return 0; } /* 5 5 89ABCDEF 3 FFFFFFFF 7 00000000 10 ABCD00FF 17 00000000 208 124 327 389 794 */