鲳数 [数位dp]

鲳数


color{red}{正解部分}

从低到高len+1len+1 位的数字为 xx, 考虑 xx 与后面 lenlen 位组成逆序对的贡献,

先考虑 x=1x=-1 时的贡献, 为 len×10lenlen imes 10^{len}, 其中每个数字出现的次数都相同,
再考虑 x[0,9]x in [0, 9], 贡献变为 len×10len×x10=len×10len1×xlen imes 10^{len} imes frac{x}{10}=len imes10^{len-1} imes x .

f[i,j]f[i, j] 表示 x=ix=i 右区间为 [0,j][0, j]ii 与 右区间 构成的贡献, 则 f[i,j]=10ji(j+1)f[i, j] = 10^{j}i(j+1),

[0,i][0, i] 位 无限制地填数 产生的总逆序对为 g[i]g[i], 则 g[i]=10g[i1]+j=09f[j,i]g[i] = 10g[i-1] + sumlimits_{j=0}^9 f[j, i] .

设当前位置为 ii, 上界为 limlim, 选择的数字为 xx, ansans 是初值为 00 的变量,
最高位为 tptp, Tong[p]Tong[p] 表示 从 最高位 到 第 i+1i+1pp 数字出现的次数,
suf[p]suf[p] 表示从 第 pp 位 到 00 位 的数字转化为十进制后的值,

现在按位 从低到高 进行处理,

  • x<limx < lim, 总共有 limlim 个符合条件的 xx, 枚举 x[0,lim)x in [0, lim),

    1. [0,i)[0, i) 内部的贡献, lim×g[i1]lim imes g[i-1]
    2. xx[0,i)[0, i) 构成的贡献: f[x,i1]f[x, i-1]
    3. (i,tp](i, tp][0,i)[0, i) 构成的贡献: j=09Tong[j]×f[j,i1]sumlimits_{j=0}^9 Tong[j] imes f[j, i-1]
    4. xx(i,tp](i, tp] 构成的贡献: 10ij=x+19Tong[j]10^i sumlimits_{j=x+1}^9Tong[j]

    所以该情况的总贡献为 lim×g[i1]+x=0lim1(f[x,i1]+j=09Tong[j]×f[j,i1]+10ij=x+19Tong[j])lim imes g[i-1] + sumlimits_{x=0}^{lim-1} left( f[x, i-1] + sumlimits_{j=0}^9 Tong[j] imes f[j, i-1] + 10^isumlimits_{j=x+1}^9Tong[j] ight), 累计进 ansans .

  • x=limx = lim, 考虑计算 xx 与前面原数位产生的逆序对贡献, 贡献为 (suf[i1]+1)×j=lim+19Tong[j](suf[i-1]+1) imessumlimits_{j=lim+1}^9 Tong[j], 累加进 ansans .


color{red}{实现部分}

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;

const int maxn = 500005;
const int mod = 998244353;

int T;
int len[2];
int g[maxn];
int pw[maxn];
int A[2][maxn];
int Tong[maxn];
int f[11][maxn];

char Cmp_1[maxn];
char Cmp_2[maxn];

void Init(){
        pw[0] = 1; for(reg int i = 1; i < maxn; i ++) pw[i] = 10ll*pw[i-1] % mod;
        for(reg int j = 0; j < maxn; j ++)
                for(reg int i = 0; i <= 9; i ++) f[i][j] = 1ll*pw[j]*i % mod *(j+1) % mod;
        for(reg int i = 1; i < maxn; i ++){
                g[i] = 10ll*g[i-1] % mod;
                for(reg int j = 0; j <= 9; j ++) g[i] = (g[i] + f[j][i-1]) % mod;
        }
}

int Solve(int fl){
        if(!len[fl]) return 0;
        ll res = 0;
        for(reg int i = 0; i < len[fl]; i ++) Tong[A[fl][i]] ++;
        int suf = 0;
        for(reg int i = 0; i < len[fl]; i ++){
                int lim = A[fl][i]; Tong[lim] --;
                if(i){
                        res += 1ll*lim*g[i-1] % mod, res -= res>=mod?mod:0;
                        for(reg int x = 0; x < lim; x ++) res += f[x][i-1], res -= res>=mod?mod:0;
                        for(reg int j = 0; j <= 9; j ++) res += 1ll*lim*Tong[j]%mod*f[j][i-1] % mod, res -= res>=mod?mod:0;
                }
                /*
                for(reg int x = 0; x < lim; x ++) 
                        for(reg int j = x+1; j <= 9; j ++) res += 1ll*pw[i]*Tong[j] % mod, res %= mod;
                */
                for(reg int j = 1; j <= 9; j ++) res += (std::min(j-1, lim-1)+1ll)*Tong[j]%mod*pw[i]%mod, res -= res>=mod?mod:0;
                for(reg int j = lim+1; j <= 9; j ++) res += (1ll*suf + 1) * Tong[j] % mod, res -= res>=mod?mod:0;
                suf = (suf + (1ll*lim*pw[i]%mod)) % mod; 
        }
        return res;
}

int main(){
        Init(); int fuck;
        scanf("%d%d", &T, &fuck);
        while(T --){
                scanf("%s%s", Cmp_1, Cmp_2);
                len[0] = strlen(Cmp_1), len[1] = strlen(Cmp_2);
                for(reg int i = 0; i < len[0]; i ++) A[0][i] = Cmp_1[len[0]-i-1] - '0';
                for(reg int i = 0; i < len[1]; i ++) A[1][i] = Cmp_2[len[1]-i-1] - '0';
                A[0][0] --; int t  = 0;
                while(A[0][t] < 0) A[0][t] += 10, A[0][++ t] --;
                if(!A[0][len[0]-1]) len[0] --;
                printf("%d
", (Solve(1) - Solve(0) + mod) % mod);
        }
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822435.html