Palindrome Function
f(n,k)=k if n is a palindrome number under k-base.
Otherwise f(n,k)=1.
Now given you 4 integers L,R,l,r,you need to caluclate the mathematics expression ∑Ri=L∑rj=lf(i,j) ∑i=LR∑j=lrf(i,j) .
When representing the k-base(k>10) number,we need to use A to represent 10,B to represent 11,C to repesent 12 and so on.The biggest number is Z(35),so we only discuss about the situation at most 36-base number.
In the following T lines,each line consists of 4 integers L,R,l,r.
( 1≤T≤105,1≤L≤R≤109,2≤l≤r≤36)
3 1 1 2 36 1 982180 10 10 496690841 524639270 5 20
Case #1: 665 Case #2: 1000000 Case #3: 447525746
这是昨天CCPC网络赛上的一道题,很多人都是用数位DP去做的,但是也可以用另一种找规律的方法去解决。
我们令solve(a,k)表示从1到a的所有数在k进制下是回文串的个数,令t=solve(R,i)-solve(L-1,i),那么答案就是∑(i=l→r)(t*i+R-L+1-t)。接下来就只要求solve(a,k)就行了,我们可以发现,如果把a表示成k进制有i位,那么前i-1位的值都是可以通过规律很快算出来的,我们令tp[m][n]表示n进制下的m位数一共有多少个回文数,然后我们列个表看一下
m 1 2 3 4 5 6 7 8 9
n
2 1 1 2 2 4 4 8 8 10
3 2 2 6 6 18 18 54 54 162
4 3 3 12 12 48 48 192 192 768
5 4 4 20 20 100 100 500 500 2500
6 5 5 30 30 180 180 1080 1080 6480
7 6 6 42 42 294 294 2058 2058 14406
8 7 7 56 56 448 448 3584 3584 28672
9 8 8 72 72 648 648 5832 5832 52488
这个规律已经很明显了,m=1,2的时候值是n-1,往后每两个就乘一下n。
根据这个关系我们可以打一个前缀和的表,然后我们现在就把前i-1位的值以O(1)的方式求了出来,然后我们就只需要判断i位的情况。我们可以找从1000...0001到a一共有多少个回文串,因为回文串一定是对称的,所以我们只需要把这个数分为两半,计算前半部分有多少种情况就行了,比如说从10001到49674在11进制的时候:
11进制每一位的权值 121 11 1
10001的前一半 1 0 0
49674的前一半 4 9 6
两者的差值 3 9 6
10001到49674回文串的个数 3*121 + 9*11 + 6*1 = 468
但是我们还要注意一下,当前三位选择的是496的时候,整个数是49694,这个是比49674要大的,是不符合要求的,所以我们要判断一下,如果不符合要求就要减一,变成467,但是事实上,答案却是468,这是为什么呢,这其实是因为按照上面这种计算方法,我们记漏了49094这种情况,前两位是49的时候,第三位有0,1,2,3,4,5,6,一共七种情况,但我们只记录了6种,所以最后还要加一,答案就是468。
我们还要判断两个特殊情况,当a<k时,a以k进制表示只有一位,那么回文串的数目就是a。pow(k, i-1) + 1代表在k进制下,i位数的最小的回文串的值,也就是1000....0001的情况,如果a小于pow(k, i-1) + 1,那么回文串的数目就是0。
∑Ri=L∑Ri=L∑Ri=L
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1e9 + 5;
const int MAXN = 105;
const int MOD = 1000000007;
const double eps = 1e-8;
const double PI = acos(-1.0);
int num[105];
int tp[105][40];
LL solve(LL a, LL k)//从1到a的所有数在k进制下是回文串的个数
{
if (a < k)
return a;
memset(num, 0, sizeof num);
int kk = a;
int i = 0;
LL ans=0;
LL res;
int flag = 0;
while (a)
{
num[i++] = a%k;
a /= k;
}
ans = tp[i-1][k];//前i-1位的回文串个数
res = 0;
if (kk < pow(k, i-1) + 1)//a比i位最小的回文串还要小
return ans;
for (int p = i-1; p >= i/2; p--)//计算回文串个数
{
res *= k;
if (p == i - 1)
res += num[p] - 1;
else
res += num[p];
}
for (int p = i/2-1; p >= 0; p--)//判断最后一个数是否满足要求
{
if (num[p] < num[i - 1 - p])//不满足要求,res-1
{
res--;
break;
}
if (num[p] > num[i - 1 - p])//满足要求
break;
}
ans += res;
return ans+1;
}
void init()//预处理tp数组,打前缀和的表
{
int res;
memset(tp, 0, sizeof tp);
for (int i = 2; i <= 36; i++)
for (int j=1;j<=35;j++)
{
tp[j][i] = tp[j - 1][i];
res = i - 1;
for (int t = 0; t < (j - 1) / 2; t++)
res *= i;
tp[j][i] += res;
}
}
int main()
{
LL a, b, c, d;
int T;
init();
scanf("%d", &T);
LL ans,a1,a2,a3,a4,qq;
for (int CAS = 1; CAS <= T; CAS++)
{
ans = 0;
scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
a--;
for (int i = c; i <= d; i++)
{
qq = solve(b, i) - solve(a, i);
ans += qq*i + (b - a - qq);
}
printf("Case #%d: ", CAS);
printf("%lld\n", ans);
}
}