HDU-6156 Palindrome Function

本文介绍了一个计算特定区间内回文数的问题,并提供了一种非传统的解决方案,通过枚举不同进制来找出回文数,避免了使用复杂的数位DP方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:定义函数f(n, k): 当n在k进制下为回文串时,f(n, k) = k; 否则 f(n, k) = 1;
给你一个区间 [L, R].对于区间的每一个数i, 计算 j 属于 l 到 r 进制的f(i,j) 的和。

分析:因为进制的区间很小,我们可以枚举每一个进制k,计算[L, R]在k进制下有多少回文数。然后结果就是区间长度减去回文串数(这些事函数值为1 的部分)加上回文串数乘k(函数值为k的部分)。思路很简单,求回文数可以用数位dp,但是比赛的时候没写数位dp,觉得乱搞就可以过。
我们找[0,n]在k进制下回文串数量的时候,可以先找到比n小的第一个回文串。因为回文串时对称的我们可以直接考虑一半。按回文串的奇偶分两种情况讨论。好像比数位dp快一些。

#include<stdio.h>
#include<algorithm>
#include<string>
#include<iostream>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#define mo 1000000007
#define mm %mo
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
ll primesize;
ll prime[N], phi[N], isP[N], miu[N];
ll a[N], len;

inline ll qucickM(ll a, ll b){
    ll c = 1;
    while(b){
        if(b & 1) c = c * b mm;
        b >>= 1;
        a = a * a mm;
    }
    return c;
}

void getR(ll t, ll k, int de){
    if(t < k){
        a[++len] = t;
        return;
    }
    getR(t / k, k, de + 1);
    a[++len] = t % k;
}

ll solve(ll t, ll k){
    if(t == 0) return 0;
    len = 0;
    getR(t, k, 1);
    if(len % 2){
        int mid = len / 2 + 1, le, re;
        for(le = mid - 1, re = mid + 1; le > 0 && re <= len && a[le] == a[re]; re++, le--);
        if(le >= 1 || re <= len) {
            if(a[le] > a[re]){
                int pos = 0;
                for(pos = mid; pos >= 0 && a[pos] == 0; pos--);
                a[pos] -= 1;
                if(pos)for(int j = pos + 1; j <= mid; j++) a[j] = k - 1;
                if(pos == 1 && a[pos] == 0){
                    len--;
                    for(int i = 1; i <= len; i++) a[i] = k - 1;
                    goto l1;
                }
            }
            for(int i = 1, j = len; j >= i; i++, j--){
                a[j] = a[i];
            }
        }  
    }else{
        int mid = len / 2, le, re;
        for(le = mid, re = mid + 1; le > 0 && re <= len && a[le] == a[re]; re++, le--);
        if(le >= 1 || re <= len) {
            if(a[le] > a[re]){
                int pos = 0;
                for(pos = mid; pos >= 0 && a[pos] == 0; pos--);
                a[pos] -= 1;
                if(pos)for(int j = pos + 1; j <= mid; j++) a[j] = k - 1;
                if(pos == 1 && a[pos] == 0){
                    len--;
                    for(int i = 1; i <= len; i++) a[i] = k - 1;
                    goto l1;
                }
            }
            for(int i = 1, j = len; j >= i; i++, j--){
                a[j] = a[i];
            }
        }    
    }
    l1:
    if(len % 2 == 0){
        ll p = 0, q = 1;
        for(int i = 1; i <= len / 2; i++){
            p = p * k + a[i];
            q = q * k;
        }
        return p + q - 1;
    }else{
        ll p = 0, q = 1;
        for(int i = 1; i <= len / 2; i++){
            p = p * k + a[i];
            q = q * k;
        }
        if(len == 1) return a[1];
        return (p - 1) * (k) + a[len / 2 + 1] + 1 + (q - 1) + k - 1;
    }
}

int main(){
    int T;
    //freopen("a.txt", "r", stdin);
    //freopen("b2.txt", "w", stdout);
    scanf("%d", &T);
    for(int kase = 1; kase <= T; kase++){
        ll L, R, l, r, ans = 0;
        scanf("%lld%lld%lld%lld", &L, &R, &l, &r);
        for(ll i = l; i <= r; i++){
            ll sl = solve(L - 1, i);
            ll sr = solve(R, i);
            ll tot = R - L + 1;
            ans += tot - (sr - sl) + i * (sr - sl);    
        }
        printf("Case #%d: %lld\n", kase, ans);
    }
    return 0;    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值