hdu-6156

Palindrome Function

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)
Total Submission(s): 809    Accepted Submission(s): 446


Problem Description
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) .
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

题意:给出L,R,l,r。求从进制l到进制r,数L与R之间回文数字个数。这道题一看就是数位dp,然后听说是模板题,其实,我码了一个数位dp,然后改了改,结果发现,其实不用数位dp也可以搞定,只需要一点预处理,就可以解决了。(其实是因为我不擅长数位dp)

题解:1、先预处理一个sum[i][j] 数组,为i进制下的j位没有限制且首位不为0的所有回文数的个数。然后做一下前缀和,就可以得到i进制下0到长度为j的回文数个数。

2、用0到R的回文数个数减去0到L-1的回文数个数就得到L到R中回文数个数。

3、对于当前询问的数字,先将其分解成当前进制下的数组,长度为len,然后现在目标即是算出在长度为len的受到限制且首位不为0的回文数个数,首先知道,在一个数字,比如654321中,前三位还没到达654前,后三位是没有限制的,于是回文数个数tmp = 654 - 99(这个99的意思是:因为你1到654中有1位数2位数3位数,所以有001这样的数,违反了我们前面定义的首位不为0,故将此部分减去),然后,此时tmp还有一点需要考虑,即当到达654时,是否可行(因为此时后三位已受到限制),这时就从中间枚举,即枚举这里的3,2,1,如果该位比其对应的那个对称数小,说明tmp-1(3小于4,不存在654456)如果该位比其对应得那个对称数大(假设原来要求的数为654500,5大于4,存在654456),则tmp不用减1,同样的,如果一直等下去,说明该树本身就是回文数,tmp一样不用减1。

4、此时我们找到sum[当前进制][长度为len-1],加上第3步求出来的tmp,就是0到该数的回文数个数。

5、按第2步做,做差相乘加和得到答案。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <utility>
#include <functional>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
using namespace std;
typedef long long ll;
const double eps = 1e-7;
const int maxn = 3e5+7;
const int INF = 1e9+7;

ll sum[40][40];
ll l,r,L,R,d,len;
ll b[40];
ll qpow(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b&1){
            ans*=a;
        }
        a*=a;
        b>>=1;
    }
    return ans;
}


ll solve(ll a,ll bit){
    if(a==0) return 1;
    len = 0;
    while(a){
        b[len++] = a%bit;
        a/=bit;
    }
    d = len - (len-1)/2 - 1;
    int nlen = (len-1)/2 + 1;
    ll tmp = 0;
    for(int i = len-1;i >= d;i--){
        tmp*=bit;
        tmp+=b[i];
    }
    tmp-=qpow(bit,nlen-1)-1;
    int flag = 1;
    for(int i = nlen-1;i >= 0;i--){
        if(b[i]>b[len-i-1]){
            flag = 1;
            break;
        }
        else if(b[i]<b[len-i-1]){
            flag = 0;
            break;
        }
    }
    if(!flag) tmp-=1;
    //cout<<tmp<<" "<<sum[bit][len-1]<<endl;
    return tmp+sum[bit][len-1];
}

int main(){
    memset(sum,0,sizeof(sum));
    for(int i = 2;i <= 36;i++){
        sum[i][1] = sum[i][0] = 1;
    }
    for(int i = 2;i <= 36;i++){
        for(int j = 1;j < 40;j++){
            sum[i][j] += qpow(i,(j-1)/2)*(i-1);
            if(j!=1) sum[i][j] += sum[i][j-1];
        }
    }
    int t;
    scanf("%d",&t);
    int I = 1;
    while(t--){
        scanf("%lld %lld %lld %lld",&L,&R,&l,&r);
        ll ans = 0;
        ll all = R-L+1;
        for(int i = l;i <= r;i++){
            ll tmp = solve(R,i)-solve(L-1,i);
            ans+=tmp*i+all-tmp;
        }
        printf("Case #%d: ",I++);
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值