题目大意
定义:f(n,k)={k1如果n在k进制下是一个回文数否则
让你求
∑i=LR∑j=lrf(i,j)
分析
这道题可以将问题简化成求:
给你一个数n(十进制),问你在k进制下不超过n的回文数有多少个
这道题我的做法有点冗余了,改了好久找了一份AC代码对拍才改过的。
设n在k进制下长度为m,用数组表示为 a[1...len] 先分类:
- 长度小于m的回文数的个数,这个比较好求,初始化以下就行
- 长度等于m的回文数的个数,又分为两类
1.首位元素小于 a[1]
2.首位元素为 a[1]
定义了一个函数Less(int ch[],int len,int k),ch[]用来保存k进制下的数
定义 num1[m][k] 表示长为m在k进制下的回文数的个数,开头可以是0
定义 num1[m][k] 表示长为m在k进制下的回文数的个数,开头不能是0
这个函数表示小于长为len小于等于ch的数的个数(需要注意的是这里的数可以是以0开头的)由于Less找的是可以以0开头的数,所以在最外层中还要减去开头是0的情况数也就是 num[len−2][k]
需要处理边界条件是len为0 1 2 的情况
总结
像这样的题,以后一定要多想想有没有简单一点的实现方式代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
#define LL long long int
const int MAXL=110;
int num1[MAXL][40];//开头可以为0
int num2[MAXL][40];//开头不能为0
int Pow(int n,int k)
{
int ans=1;
for(int i=1;i<=k;i++)ans*=n;
return ans;
}
void Make_num1()
{
for (int i=1;i<=100;i++)
for (int j=2;j<=36;j++)
{
if (i==1) num1[i][j]=j;
else num1[i][j]=Pow(j,(i+1)/2);
}
}
void Make_num2()
{
for (int i=1;i<=100;i++)
for (int j=2;j<=36;j++)
{
if (i==1) num2[i][j]=j;
else num2[i][j]=(j-1)*Pow(j,(i-1)/2);
}
}
int Make_ch(int ch[],int n,int k)///把1个十进制的数转换成k进制
{
int len=0;
while (n)
{
ch[++len]=n%k;
n/=k;
}
return len;
}
bool is_Palindrome(int ch[],int len)
{
bool flag=true;
for (int i=1;i<=len/2;i++)
if (ch[i]!=ch[len-i+1])
{
flag=false;
break;
}
return flag;
}
int Less(int ch[],int len,int k)//k进制下不超过n首可以为0的个数
{
int res=0;
if (len==0) return 1;
if (len==1) return (ch[len]+1);
if (len==2)
{
if(ch[len]<=ch[1])return ch[len]+1;
else return ch[len];
}
///长度等于len且第一位比当前小的回文数 边界
res+=(ch[len]*num1[len-2][k]);
///长度等于len且第一位等于当前第一位的回文数
int p=0;
int temp[MAXL];int len2=0;
for (int i=2;i<=len-1;i++)
{
temp[++len2]=ch[i];
p+=ch[i]*Pow(k,len2-1);
}
res+=Less(temp,len2,k);
if (ch[1]<ch[len]&&is_Palindrome(temp,len2))
res--;
return res;
}
int Work(int n,int k)
{
int ans=0;
int ch[MAXL];
int len=Make_ch(ch,n,k);
if (len==0) return 1;
if (len==1) return (ch[1]+1);
if (len==2)
{
if(ch[len]<=ch[1])return ch[len]+k;
else return ch[len]-1+k;
}
ans=Less(ch,len,k);
for (int i=0;i<len;i++) ans+=num2[i][k];///长度小于len的回文数
ans-=num1[len-2][k];//Less(p,k);
return ans;
}
int main()
{
//freopen("data.txt","r",stdin);
//freopen("out1.txt","w",stdout);
Make_num1();
Make_num2();
int T,L,R,l,r,j,cnt=0;
LL ans=0;
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d%d",&L,&R,&l,&r);
ans=0;
for (j=l;j<=r;j++)
{
int x=Work(R,j)-Work(L-1,j);
int sum=R-L+1;
ans+=(x*j+sum-x);
}
printf("Case #%d: %lld\n",++cnt,ans);
}
return 0;
}
/*
3
1 1 2 36
1 982180 10 10
496690841 524639270 5 20
*/