LightOJ - 1205 - 回文串计数,数位dp

题目大意:

求区间回文数,区间比较大.

题目思路:

区间大了之后我们自然要想到数位dp解决.

对一个数字,dp它的前 n 2 \frac{n}{2} 2n位。考虑到递归出口时,怎样算合法.

令上界为 X X X,前一半数为 x 1 x_1 x1,后一半数为 x 2 x_2 x2,dp出来的前一半数为: Y Y Y.

1.若前半数位没有顶到上界,则 Y < x 1 Y < x_1 Y<x1,则 Y + r e v e r s e ( Y ) < x 1 + x 2 = X Y+reverse(Y) < x_1+x_2 = X Y+reverse(Y)<x1+x2=X. 一定合法 (+代表拼接).

2.若顶到上界,则 Y = = x 1 Y == x_1 Y==x1,则当 r e v e r s e ( Y ) ≤ x 2 reverse(Y) \leq x_2 reverse(Y)x2时才合法.

这样我们 d p dp dp转移的时候除了维护数位,是否顶到上界以外,还需要记录 r e v e r s e ( Y ) reverse(Y) reverse(Y) x 2 x_2 x2的大小关系:

每进行到下一个数位,在 Y Y Y的尾部增加一个数字,即在 r e ( Y ) re(Y) re(Y)顶部增添一个数字 w w w
令之前的大小关系为 s t a sta sta(=1代表之前的 r e ( Y ) ≤ x 2 re(Y)\leq x_2 re(Y)x2,=0反之)

考虑状态转移:
w < X [ n − s t e p + 1 ] w<X[n-step+1] w<X[nstep+1] s t a : = 1 sta:=1 sta:=1
w = = X [ n − s t e p + 1 ] w==X[n-step+1] w==X[nstep+1] s t a sta sta不变
w > X [ n − s t e p + 1 ] w>X[n-step+1] w>X[nstep+1] s t a = 0 sta=0 sta=0

递归出口返回 ( ! l i m ) ∣ ∣ s t a (!lim) || sta (!lim)sta

还有一些细节看代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
ll a[maxn] , n , s[50] , dp[20][2][2];
string dig;
ll dfs (int step , int lim , int sta , int n){
    if (n & 1){
        if (step * 2 - 1 > n){
            return (!lim) || sta;
        }
    }else {
        if (step * 2 > n){
            return (!lim) || sta;
        }
    }
    ll &x = dp[step][lim][sta];
    if ( ~x ) return x;
    int up , low;
    low = (step == 1 ? 1 : 0);
    up = (lim ? dig[step] - '0' : 9);
    ll ans = 0;
    int dm = dig[n - step + 1] - '0';
    for (int i = low ; i <= up ; i++){
        int nsta;
        if (i < dm) nsta = 1;
        else if (i == dm) nsta = sta;
        else nsta = 0;
        ans += dfs(step + 1 , lim && i == up , nsta , n);
    }
    return x = ans;
}
ll calc (ll x , bool ok){
    if (x == -1) return 0;
    memset(dp , -1 , sizeof dp);
    dig = to_string(x);
    n = dig.size();
    dig = '#' + dig;
    ll ans = 0;
    if (ok) ans = s[n - 1];
    ans += dfs(1 , true , 1 , n);
    if (n == 1) ans++;
    return ans;
}
int main()
{
    ll base = 0;
    for (int i = 1 ; i <= 17 ; i++)
        s[i] = s[i - 1] + calc(base = base * 10 + 9 , false);
    ios::sync_with_stdio(false);
    int t; cin >> t;
    int cnt = 0;
    while (t--){
        ll x , y; cin >> x >> y;
        if (x > y) swap(x , y);
        cout << "Case " << (++cnt) << ": ";
        cout << calc(y , true) - calc(x - 1 , true) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值