吐槽:比赛的时候智障了。。敲了一个多小时,最后有个地方忘了减1。。导致没做出来。。雪崩,还是数位DP做的比较少。
**题意:**f(n,k)表示n在k进制下是否为回文串,若是,f(n,k)=k,否则f(n,k)=1。
思路:数位DP,枚举进制,再枚举长度,可以搞出前一半,后一半翻转就好了。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll ;
ll dp[38][40][40]; //表示i进制下对称点是p,当前位置为pos的满足题意的个数。
int DIG[38][40];
ll dfs(int pos,int p , int lead,int jinzhi,int limit)
{
if(pos < p) //若过了对称点就返回
return 1;
if(!limit &&!lead && dp[jinzhi][p][pos] != -1)
return dp[jinzhi][p][pos];
int end = limit ? DIG[jinzhi][pos] : jinzhi-1;
int ret = 0;
int st = 0 ;
if(lead) st++; //lead表示是否为第一个,第一个不能为0,0我们后面考虑
for(int i = st ; i <= end ; i++)
ret += dfs(pos-1,p,0,jinzhi,limit&&(end==i));
if(!limit && !lead)
dp[jinzhi][p][pos] = ret;
return ret;
}
ll solve(ll n, ll l, ll r)
{
ll ans = 0 ;
ll tmp ;
ll now = 0; //now记录了是回文串的个数
for(int i = l ; i <= r ; i++) //枚举进制
{
int len = 0 ;
tmp = n ;
while(tmp) //分解n
{
DIG[i][len++] = tmp % i ;
tmp /= i ;
}
for(int j = 1 ; j <= len ; j++) //枚举长度
{
tmp = j/2; //求出前面的长度
ll t1 ;
if(j == len) t1 = dfs(j-1,tmp,1,i,1) ; //若长度为len,考虑边界
else t1 = dfs(j-1,tmp,1,i,0);
ans += t1*i;
now += t1 ;
if(j == len) //判边界条件
{
int ff = -1 ;
for(int k = tmp-1 ; k >= 0 ; k--)
if(DIG[i][k] < DIG[i][len-1-k])
{
ff = 1 ; break ;
}
else if(DIG[i][k] > DIG[i][len-1-k])
{
ff = 0 ; break ;
}
if(ff == 1) //若原数字的前一半翻转过去后比原串大,就减去这种情况
{
ans -= i ;
now -- ; //就是这里,,我忘了减1。。
}
}
}
ans += i ; //0在这里加上去
}
ans += n*(r-l+1)-now ; //全部数减去回文串的个数,就是价值为1的数的个数
return ans ;
}
int main()
{
int t ;
cin>> t ;
int n1,n2,l,r;
int count1 = 0 ;
memset(dp,-1,sizeof(dp));
while(t--)
{
scanf("%d%d%d%d",&n1,&n2,&l,&r);
ll ans = solve(n2,l,r) - solve(n1-1,l,r);
printf("Case #%d: %lld\n",++count1,ans);
}
return 0;
}