zoukankan      html  css  js  c++  java
  • [组合数学] HDU 7060 Separated Number

    题目大意

    定义一个十进制数的 (k) 分割为将这个数的数位分割成 (k) 个区间,如(11)(4)(514)就是114514的一种3分割。 一种 (k) 分割的价值就是把每个区间重新看成一个十进制数,(k) 个区间相加得到的值。如(11)(4)(514)的价值为11+4+514=529。给一个长为 (n(nleq 10^6)) 的十进制数和 (k(1leq kleq n)),对于所有的 (k'leq k),求它的所有 (k') 分割的价值之和。

    题解

    不妨枚举数位,计算每个数位的贡献。设该十进制数从高到低第 (i) 位为 (a_i),那么我们可以枚举 (a_i) 此时属于分割中的哪个数,设当前 (a_i) 属于 (a_L)(a_R) 这个区间组成的数中,那么 (a_i) 此时的价值为 (a_i10^{R-i}),那么还剩下 (n-(R-L+1)) 个数要分成小于等于 (k-1) 份,可以使用隔板法解决。注意到当 (1<L)(R<n) 时,([a_L,a_R]) 的左边和右边都存在数,所以剩下 (n-(R-L+1)) 个数中间其实相当于已经被插入了 ([a_L,a_R]) 这一块隔板,因此方案数为 (sum_{j=0}^{k-3}inom{n-(R-L+1)-2}{j})。若 (L=1)(R=n),则没有剩下的数,对剩下的数进行分割的方案数为1。除以上两种情况以外若 (L=1)(R=n),则只在 ([a_L,a_R]) 的左边或右边存在数,相当于 (n-(R-L+1)) 个数之间还没有插入隔板,方案数为 (sum_{j=0}^{k-2}inom{n-(R-L+1)-1}{j}),将四种情况相加,即得答案。

    [ans_1=sum_{L=2}^{n-1}sum_{R=L}^{n-1}left(sum_{i=L}^Ra_i10^{R-i} ight)cdotleft(sum_{j=0}^{k-3}inom{n-(R-L+1)-2}{j} ight) (L>1land R<n) ]

    [ans_2=sum_{i=1}^n a_i10^{n-i} (L=1 land R=n) ]

    [ans_3=sum_{R=1}^{n-1}left(sum_{i=1}^Ra_i10^{R-i} ight)cdotleft(sum_{j=0}^{k-2}inom{n-R-1}{j} ight) (L=1 land R<n) ]

    [ans_4=sum_{L=2}^nleft(sum_{i=L}^na_i10^{n-i} ight)cdotleft(sum_{j=0}^{k-2}inom{L-2}{j} ight) (L>1land R=n) ]

    [ans=ans_1+ans_2+ans_3+ans_4 ]

    考虑怎么加速计算,不妨设

    [f(n)=sum_{i=1}^n a_i10^{-i}\ g(n)=sum_{i=1}^n 10^if(i)\ h(n,m)=sum_{i=0}^minom{n}{i} ]

    则有

    [ans_2=sum_{i=1}^n a_i10^{n-i}=10^nsum_{i=1}^na_i10^{-i}=10^nf(n) ]

    [ans_3=sum_{R=1}^{n-1}left(sum_{i=1}^Ra_i10^{R-i} ight)cdotleft(sum_{j=0}^{k-2}inom{n-R-1}{j} ight)\ =sum_{R=1}^{n-1}10^Rleft(sum_{i=1}^Ra_i10^{-i} ight)cdot h(n-R-1,k-2)\ =sum_{R=1}^{n-1}10^Rf(R)cdot h(n-R-1,k-2)\ ]

    [ans_4=sum_{L=2}^nleft(sum_{i=L}^na_i10^{n-i} ight)cdotleft(sum_{j=0}^{k-2}inom{L-2}{j} ight)\ =sum_{L=2}^n10^nleft(sum_{i=L}^na_i10^{-i} ight)cdot h(L-2,k-2)\ =sum_{L=2}^n10^n(f(n)-f(L-1))cdot h(L-2,k-2) ]

    [ans_1=sum_{L=2}^{n-1}sum_{R=L}^{n-1}left(sum_{i=L}^Ra_i10^{R-i} ight)cdotleft(sum_{j=0}^{k-3}inom{n-(R-L+1)-2}{j} ight)\ =sum_{len=1}^{n-2}sum_{R=len+1}^{n-1}left(sum_{i=R-len+1}^Ra_i10^{R-i} ight)cdotleft(sum_{j=0}^{k-3}inom{n-len-2}{j} ight)\ =sum_{len=1}^{n-2}h(n-len-2,k-3)sum_{R=len+1}^{n-1}left(sum_{i=R-len+1}^Ra_i10^{R-i} ight)\ =sum_{len=1}^{n-2}h(n-len-2,k-3)sum_{R=len+1}^{n-1}10^Rleft(sum_{i=R-len+1}^Ra_i10^{-i} ight)\ =sum_{len=1}^{n-2}h(n-len-2,k-3)sum_{R=len+1}^{n-1}10^R(f(R)-f(R-len))\ =sum_{len=1}^{n-2}h(n-len-2,k-3)left[left(sum_{R=len+1}^{n-1}10^Rf(R) ight)-left(sum_{R=len+1}^{n-1}10^Rf(R-len) ight) ight]\ =sum_{len=1}^{n-2}h(n-len-2,k-3)left[left(sum_{R=len+1}^{n-1}10^Rf(R) ight)-10^{len}left(sum_{R=len+1}^{n-1}10^{R-len}f(R-len) ight) ight]\ =sum_{len=1}^{n-2}h(n-len-2,k-3)left[g(n-1)-g(len)-10^{len}g(n-1-len) ight]\ ]

    至此,如果我们已知 (f(n),g(n),h(n,m)),我们就可以以 (O(n)) 的时间复杂度求出 (ans_1,ans_2,ans_3,ans_4)。而 (f(n))(g(n)) 显然都可以 (O(n)) 求出,关键是 (h(n,m))。发现式中 (h(n,m)) 仅有 (h(w,k-2))(h(w,k-3)) 这两种形式,(k) 是常数。

    (h(n,m)) 表示 (n) 个位置选不超过 (m) 个位置的方案数,我们想从 (h(n-1,m)) 得到 (h(n,m)),考虑容斥原理:

    (h(n-1,m))(1sim n-1) 这些位置选不超过 (m) 个位置的方案数,也可以是 (2sim n) 这些位置选不超过 (m) 个位置的方案数,将两者相加,重合部分是位置 (1) 和位置 (n) 都没有被选,且 (2sim n-1) 选了不超过 (m) 个位置的方案数,即 (sum_{i=0}^minom{n-2}{i})。缺少的部分是位置 (1) 和位置 (n) 都被选了,且 (2sim n-1) 选了不超过 (m-2) 个位置的方案数,即 (sum_{i=0}^{m-2}inom{n-2}{i})

    所以有

    [h(n,m)=2 imes h(n-1,m)-sum_{i=0}^minom{n-2}{i}+sum_{i=0}^{m-2}inom{n-2}{i}\ =2 imes h(n-1,m)-inom{n-2}{m-1}-inom{n-2}{m}\ =2 imes h(n-1,m)-inom{n-1}{m} ]

    所以我们同样可以 (O(n)) 递推出所有的 (h(w,k-2))(h(w,k-3))

    最终,本题的时间复杂度为 (O(n))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define LL long long
    
    const LL MOD = 998244353LL;
    const int maxn = 1000010;
    LL inv[maxn], fact[maxn], finv[maxn], ten[maxn], f[maxn], g[maxn], h1[maxn], h2[maxn];
    char s[maxn];
    int T, n, k;
    
    void Init() {
        inv[1] = fact[0] = fact[1] = finv[0] = finv[1] = 1;
        ten[0] = 1; ten[1] = 10;
        for (int i = 2; i <= 1000000; ++i) {
            inv[i] = ((-(MOD / i) * inv[MOD % i]) % MOD + MOD) % MOD;
            fact[i] = fact[i - 1] * i % MOD;
            finv[i] = finv[i - 1] * inv[i] % MOD;
            ten[i] = ten[i - 1] * 10 % MOD;
        }
    }
    
    inline LL C(LL n, LL m) {
        if (m > n) return 0;
        if (m == n || n == 0) return 1;
        return fact[n] * finv[m] % MOD * finv[n - m] % MOD;
    }
    
    LL calc() {
        LL ans = ten[n] * f[n] % MOD;
        if (k == 1) return ans;
        for (int R = 1; R <= n - 1; ++R)
            ans = (ans + ten[R] * f[R] % MOD * h1[n - R - 1] % MOD) % MOD;
        for (int L = 2; L <= n; ++L)
            ans = ((ans + ten[n] * (f[n] - f[L - 1]) % MOD * h1[L - 2] % MOD) % MOD + MOD) % MOD;
        if (k == 2) return ans;
        for (int len = 1; len <= n - 2; ++len) {
            LL temp = (g[n - 1] - g[len]) % MOD;
            temp = (temp - ten[len] * g[n - 1 - len] % MOD) % MOD;
            ans = ((ans + h2[n - len - 2] * temp % MOD) % MOD + MOD) % MOD;
        }
        return ans;
    }
    
    int main() {
        Init();
        scanf("%d", &T);
        while (T--) {
            scanf("%d", &k);
            scanf("%s", s + 1);
            n = strlen(s + 1);
            LL ten_inv = inv[10];
            for (int i = 1; i <= n; ++i) {
                f[i] = (f[i - 1] + ten_inv * (s[i] - '0') % MOD) % MOD;
                ten_inv = ten_inv * inv[10] % MOD;
            }
            h1[0] = h2[0] = 1;
            for (int i = 1; i <= n; ++i) {
                g[i] = (g[i - 1] + f[i] * ten[i] % MOD) % MOD;
                if (k >= 2) h1[i] = (((h1[i - 1] << 1) % MOD - C(i - 1, k - 2)) % MOD + MOD) % MOD;
                if (k >= 3) h2[i] = (((h2[i - 1] << 1) % MOD - C(i - 1, k - 3)) % MOD + MOD) % MOD;
            }
            printf("%lld
    ", calc());
        }
        return 0;
    }
    
  • 相关阅读:
    java.io.Serializable浅析
    SSH和SSM的区别
    [转]github详细教程
    GITHUB的使用
    常用端口-小结
    DNS的解析原理
    windows快捷键-小结
    ip地址0.0.0.0是什么意思
    windows插件框架下载地址
    redis和mongodb
  • 原文地址:https://www.cnblogs.com/AEMShana/p/15136535.html
Copyright © 2011-2022 走看看