HDU-6093 Rikka with Number(计数/数位dp)

传送门:HDU-6093

题意:如果一个数在某一进制d下所有位是0~d-1的排列,则称这个数为“好数”,问[L,R]之间有多少个“好数”

题解:计数/数位dp

由于L和R很大(L,R<=10^5000)因此需要用大数模拟,用[1,R]内的数量减去[1,L]内的数量即可。


首先可以猜一个结论:每一个数之多只在一个d进制下能形成0~d-1的全排列。其证明也很简单:如果一个数在d进制下是“好数”,那么这个数一定在(d^(d-1),d^d]之间,因此d进制和d-1进制的区间是不想交的,猜想得证。


然后可以打表得出每一进制下数字的长度,如果d进制数字长度小于n的数字长度则区间[1,n]一定可以包含所有d进制的“好数”(注意:当d=3或d=4时要特判一下,因为d=3时区间为[11,27],d=4时区间为[75,256]都包含了长度为2的数字


易知d进制的所有“好数”有d!-(d-1)!,因此只要特殊处理临界状态的计数即可。这个计数和数位dp差不多,具体看代码。要注意的是当枚举到最低位时需要特殊处理

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MX  = 7e3 + 5;
const LL mod = 998244353;
const int MAXN = 9999;
const int DLEN = 4;
class Big {
public:
    int a[MX], len;
    Big(const int b = 0) {
        int c, d = b;
        len = 0;
        memset(a, 0, sizeof(a));
        while (d > MAXN) {
            c = d - (d / (MAXN + 1)) * (MAXN + 1);
            d = d / (MAXN + 1);
            a[len++] = c;
        }
        a[len++] = d;
    }
    Big(const char *s) {
        int t, k, index, L, i;
        memset(a, 0, sizeof(a));
        L = strlen(s);
        len = L / DLEN;
        if (L % DLEN) len++;
        index = 0;
        for (i = L - 1; i >= 0; i -= DLEN) {
            t = 0;
            k = i - DLEN + 1;
            if (k < 0) k = 0;
            for (int j = k; j <= i; j++) t = t * 10 + s[j] - '0';
            a[index++] = t;
        }
    }
    bool operator>(const Big &T)const {
        int ln;
        if (len > T.len) return true;
        else if (len == T.len) {
            ln = len - 1;
            while (a[ln] == T.a[ln] && ln >= 0) ln--;
            if (ln >= 0 && a[ln] > T.a[ln]) return true;
            else return false;
        } else return false;
    }
    bool operator==(const Big &T)const {
        int ln;
        if (len == T.len) {
            ln = len - 1;
            while (a[ln] == T.a[ln] && ln >= 0) ln--;
            return ln < 0;
        } else return false;
    }
    Big operator-(const Big &T)const {
        int i, j, big;
        bool flag;
        Big t1, t2;
        if (*this > T) {
            t1 = *this;
            t2 = T;
            flag = 0;
        } else {
            t1 = T; t2 = *this; flag = 1;
        }
        big = t1.len;
        for (i = 0; i < big; i++) {
            if (t1.a[i] < t2.a[i]) {
                j = i + 1;
                while (t1.a[j] == 0) j++;
                t1.a[j--]--;
                while (j > i) t1.a[j--] += MAXN;
                t1.a[i] += MAXN + 1 - t2.a[i];
            } else t1.a[i] -= t2.a[i];
        }
        t1.len = big;
        while (t1.a[t1.len - 1] == 0 && t1.len > 1) {
            t1.len--;
            big--;
        }
        if (flag) t1.a[big - 1] = 0 - t1.a[big - 1];
        return t1;
    }
    int operator%(const int &b)const {
        int i, d = 0;
        for (int i = len - 1; i >= 0; i--) d = ((d * (MAXN + 1)) % b + a[i]) % b;
        return d;
    }
    Big operator/(const int &b)const {
        Big ret;
        int i, down = 0;
        for (int i = len - 1; i >= 0; i--) {
            ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
            down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
        }
        ret.len = len;
        while (ret.a[ret.len - 1] == 0 && ret.len > 1) ret.len--;
        return ret;
    }
    void print() {
        printf("%d", a[len - 1]);
        for (int i = len - 2; i >= 0; i--) printf("%04d", a[i]);
    }
};
LL f[MX];
char L[MX], R[MX];
int n, d, a[MX], c[MX], e[MX], vis[MX], t[MX];
bool cmp(int a[], int b[]) {
    for (int i = d; i >= 1; i--) if (a[i] != b[i]) return a[i] > b[i];
    return 1;
}
void get_len(Big b) {
    n = (b.len - 1) * 4;
    if (b.a[b.len - 1] >= 1000) n += 4;
    else if (b.a[b.len - 1] >= 100) n += 3;
    else if (b.a[b.len - 1] >= 10) n += 2;
    else n++;
}
LL cal(Big b, int d) {
    int len = 0;
    LL ans = 0;
    while (b > 0) a[++len] = b % d, b = b / d;
    for (int i = d; i >= 1; i--) c[i] = i - 1, e[i] = d - i;
    swap(e[d], e[d - 1]);
    if (len > d || cmp(a, c)) ans = (ans + f[d] - f[d - 1]) % mod;
    else if (len == d && cmp(a, e)) {
        LL cnt;
        memset(vis, 0, sizeof(vis));
        for (int i = len; i >= 1; i--) {
            if (i == len) cnt = a[i] - 1;
            else {
                cnt = 0;
                for (int j = 0; j < a[i]; j++) if (!vis[j]) cnt++;
                if (i == 1) for (int j = 0; j <= a[i]; j++) if (!vis[j]) cnt = 1;
            }
            ans = (ans + cnt * f[i - 1]) % mod;
            if (vis[a[i]]) break;
            vis[a[i]] = 1;
        }
    }
    return ans;
}
LL solve(Big b) {
    if (b == 0 || b == 1) return 0;
    get_len(b);
    LL ans = 0;
    if (n == 2) {
        if (b > 10) d = 3;
        if (b > 74) d = 4;
    } else
        for (d = 2; d < MX && t[d] < n; d++);
    for (int i = 2; i < d - 1; i++) ans = (ans + f[i] - f[i - 1] + mod) % mod;
    if (d > 2) ans = (ans + cal(b, d - 1)) % mod;
    ans = (ans + cal(b, d)) % mod;
    return ans;
}
void init() {
    f[0] = 1;
    for (int i = 1; i < MX; i++) f[i] = f[i - 1] * i % mod;
    for (int i = 2; i < MX; i++) t[i] = i * log10(1.0 * i);
}
int main() {
    int T;
    //freopen("in.txt", "r", stdin);
    init();
    cin >> T;
    while (T--) {
        scanf("%s%s", L, R);
        Big l = L, r = R;
        printf("%lld\n", (solve(r) - solve(l - 1) + mod) % mod);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值