2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6156 数位DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6156
题意:如题。
解法:数位DP,暴力枚举进制之后,就转化成了求L,R区间的回文数的个数,这个直接做一个数位DP就好了。dp[jz][start][cur][state]表示jz进制下以start位起始到cur位状态为state(1表示已经回文,0表示没有回文)时回文数的个数。
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL dp[40][40][40][2]; LL temp[100], num[100]; //dp[start][cur][state]表示以start位起始到cur位状态为state(1表示已经回文,0表示没有回文)时回文数的个数 LL dfs(int jz, int start, int cur, bool state, bool jud){ if(cur<0) return state; if(!jud&&dp[jz][start][cur][state]!=-1) return dp[jz][start][cur][state]; int mx = jud?num[cur]:jz-1; LL ret=0; for(int i=0; i<=mx; i++){ temp[cur]=i;//枚举该位的值 if(start==cur&&i==0)//前导0 ret += dfs(jz,start-1,cur-1,state,jud&&i==mx); else if(state&&cur<(start+1)/2)//已经构成回文串 ret+=dfs(jz,start,cur-1,temp[start-cur]==i,jud&&i==mx); else//尚未构成回文串 ret+=dfs(jz,start,cur-1,state,jud&&i==mx); } if(!jud) dp[jz][start][cur][state]=ret; return ret; } LL f(LL n, LL jz){ int len = 0; while(n){ num[len++]=n%jz; n/=jz; } num[len]=0; return dfs(jz,len-1,len-1,1,1); } LL L,R,l,r; int main() { int T,ks=0; scanf("%d", &T); memset(dp,-1,sizeof(dp)); while(T--) { LL ans = 0; scanf("%lld%lld%lld%lld",&L,&R,&l,&r); for(LL i=l; i<=r; i++){ if(L>R) swap(L,R); LL ret = f(R,i)-f(L-1,i); ans += ret*i+(R-L+1-ret); } printf("Case #%d: %lld\n", ++ks, ans); } return 0; }