HDU-6156 Palindrome Function(找规律)

                             Palindrome Function

As we all know,a palindrome number is the number which reads the same backward as forward,such as 666 or 747.Some numbers are not the palindrome numbers in decimal form,but in other base,they may become the palindrome number.Like 288,it’s not a palindrome number under 10-base.But if we convert it to 17-base number,it’s GG,which becomes a palindrome number.So we define an interesting function f(n,k) as follow: 
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=Lrj=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.
Input
The first line consists of an integer T,which denotes the number of test cases. 
In the following T lines,each line consists of 4 integers L,R,l,r. 
( 1T105,1LR109,2lr36)
Output
For each test case, output the answer in the form of “Case #i: ans” in a seperate line.
Sample Input
3
1 1 2 36
1 982180 10 10
496690841 524639270 5 20
Sample Output
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  

        

         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=LRi=LRi=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);
	}
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值