题意:给你一个数据范围[L, R]问在这个数据范围内的x的f(x, k)之和为多少。f(x, k)如果x转换成k进制的时候是回文串,则f(x,k) = k,反之f(x, k) = 1。例如f(288, 17) ,288在17进制上是GG,为回文串,则f(288, 17) = 17;
刚开始做的时候,思路特别混乱,变量加的太多,导致代码乱七八糟,后来思路真的太乱了,就去看了一下大佬的代码,才明白,不要判断那么多条件,果然还是太久没做,还是要多练练啊。。A了之后仔细想想其实也没有很为难我。。可能脑子不太清醒,判断条件写的乱七八糟吧。。
思路:dp[位数][数字长度][进制数][是否为回文] ,因为进制数最小是2,2^30 > 1e9,所以我开了dp[40][40][40][2]。其它的看代码注释吧。
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
ll dp[40][40][40][2];
int num[40]; //记录回文串pos位置的当前数字
int a[40]; //记录每一位数字限制
ll dfs(int pos,int len,int k,int sta,int limit){
if(pos == -1)
return sta?k:1;
if(!limit && dp[pos][len][k][sta] != -1)
return dp[pos][len][k][sta];
int up = limit?a[pos]:k-1; //这时候是k进制,所以如果没有限制最大为k-1,不要和10进制弄混了
ll ans = 0;
for(int i = 0; i <= up; i++){ //枚举每一位的数字
num[pos] = i; //记录pos位的数字为多少,后面判断回文
if(i == 0 && len == pos) //如果len == pos说明前面都是0, 如果i == 0,则len-1。
ans += dfs(pos-1, len-1, k, sta, limit&&i==a[pos]);
else if(sta && pos < (len + 1)/2) //如果sta == 1说明前面的字母都还符合回文串或则还没枚举到一半的位置,pos 判断是否枚举到一半以后的位置
ans += dfs(pos-1, len, k, i == num[len-pos], limit&&i==a[pos]);
else //要么sta == 0,后面的不管多少都不会是回文串,要么pos >= (len+1)/2还没到判断的时候,所以sta不变
ans += dfs(pos-1, len, k, sta, limit&&i == a[pos]);
}
if(!limit)
dp[pos][len][k][sta] = ans;
return ans;
}
ll slove(int x,int l,int r){
ll ans = 0, x0 = x;;
for(int i = l; i <= r; i++){ //枚举每一进制
int pos = 0;
x = x0; //这一步别忘了
while(x){
a[pos++] = x%i; //这里是取余进制数,不是10,别弄混了
x /= i;
}
ans += dfs(pos-1, pos-1, i, 1, 1);
}
return ans;
}
int main(){
int t, L, R, l, r, Case = 1;;
scanf("%d",&t);
memset(dp, -1, sizeof(dp));
while(t--){
scanf("%d%d%d%d",&L,&R,&l,&r);
printf("Case #%d: %lld\n",Case++,slove(R, l, r)- slove(L-1, l, r)); //L记得-1
}
return 0;
}