zoukankan      html  css  js  c++  java
  • 2017 多校5 hdu 6093 Rikka with Number

    2017 多校5 Rikka with Number (数学 + 数位dp)

    题意:

    统计([L,R])内 有多少数字 满足在某个(d(d>=2))进制下是(d)的全排列的

    (1 <= L <= R <= 10^{5000})

    题解:

    首先转化成计算小于等于 (N)的好数有多少个。因为 (n^n<(n+1)^n)

    ​​而对于 (n) 进制下的任何一个好数 (K),都有 (n^{n-1}<K<n^n ​)
    ​​
    ​​ ,所以每一个进制下好数的大小区间是不相交的。
    不难发现 (d) 进制下好数的个数为 (d!-(d-1)!)
    因此我们只需要计算在临界的 (d) 进制下,好数的个数就可以了。关于临界的 (d),可以用对数估计位数得到。

    (d) 进制下小于等于 (N) 的好数个数,先讲 (N) 转化成 (d) 进制,然后做一个类似康托展开的过程就可以了,因为数据范围很小,所以每一步操作都可以暴力进行。

    时间复杂度 (O(|R|^2))

    计算临界状态就用类似数位dp的做法,枚举数字,当不在上界的时候,后面的数字是可以取一个排列,直接算就好了
    这样计数是线性的

    ps: 做完这题还收获了一份大数模板

    #include<bits/stdc++.h>
    #define LL long long
    #define P pair<int,int>
    #define ls(i) seg[i].lc
    #define rs(i) seg[i].rc
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define ls rt<<1
    #define rs (rt<<1|1)
    using namespace std;
    const int mod = 998244353;
    const int MX = 5000;
    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("%d", a[i]);
        }
        int change(int m,int *s){ ///将大数转化成m进制,保存在字符串s 范围从1到len,并返回长度
            int B[MX];
            int Le=0; memcpy(B,a,sizeof a);
            int tmp = len - 1;
            while (tmp >= 0){
                int x=0;
                for (int i = tmp;i >= 0;i--){
                    int pre=x; x=(x*(MAXN+1)+B[i])%m; B[i]=(pre*(MAXN+1)+B[i])/m;
                }
                s[++Le]=x; while(tmp >= 0&&B[tmp]==0) tmp--;
            }
            return Le;
        }
    };
    char sl[5050],sr[5050];
    int f[2000];
    int digit[MX];
    int vis[MX];
    int d;
    void init(){
        f[0] = 1;
        for(int i = 1;i < 2000;i++) f[i] = 1LL * i * f[i-1] % mod;
    }
    void add(int &x,int y){
        x += y;
        if(x >= mod) x -= mod;
    }
    int dfs(int pos,int rbound,int leading_zero){
        if(pos == 0) return 1;
        if(!rbound) return f[pos];
        int r = rbound?digit[pos]:d - 1;
        int ans = 0,l = 0;
        if(leading_zero) l++;
        for(int i = l;i <= r;i++){
            if(!vis[i]){
                vis[i] = 1;
                add(ans,dfs(pos - 1, i == r && rbound,0));
                vis[i] = 0;
            }
        }
        return ans;
    }
    int calc(Big x){
       if(x == 0) return 0;
       int len = x.change(10,digit),ans = 0;
       for(d = 2;;d++){ ///先找到最大的d  在十进制下位数<=len 在d前面的可以直接算
           if((int)((d+1)*log10(d+1))+1>len) break;
           add(ans, 1LL*(d-1)*f[d-1]%mod);
       }
       while(1){///暴力枚举d,最多2个,把x转化为d进制的位数和d比较,若位数小于d,超过临界跳出
           int pos = x.change(d,digit);
           if(pos < d) break;
           if(pos > d) add(ans, 1LL*(d-1)*f[d-1]%mod);
           else {
            memset(vis,0,sizeof(vis));
            add(ans,dfs(pos,1,1));
           }
           d++;
       }
       return ans;
    }
    int main(){
        init();
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%s%s",sl,sr);
            Big l = sl, r = sr;
            printf("%d
    ",(mod+calc(r)-calc(l-1))%mod);
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    Oracle中的4大空值处理函数用法举例
    PyCharm安装
    Python安装与环境变量的配置
    多层分组排序问题
    将时间点的数据变成时间段的数据
    根据状态变化情况,求最大值和最小值
    ubuntu 源码安装 swig
    CSDN博客排名第一名,何许人也
    thinkPHP的常用配置项
    拔一拔 ExtJS 3.4 里你遇到的没遇到的 BUG(1)
  • 原文地址:https://www.cnblogs.com/jiachinzhao/p/7348321.html
Copyright © 2011-2022 走看看