1. 程式人生 > 其它 >[組合數學] HDU 7060 Separated Number

[組合數學] HDU 7060 Separated Number

題目大意

定義一個十進位制數的 \(k\) 分割為將這個數的數位分割成 \(k\) 個區間,如(11)(4)(514)就是114514的一種3分割。 一種 \(k\) 分割的價值就是把每個區間重新看成一個十進位制數,\(k\) 個區間相加得到的值。如(11)(4)(514)的價值為11+4+514=529。給一個長為 \(n(n\leq 10^6)\) 的十進位制數和 \(k(1\leq k\leq 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}\binom{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}\binom{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}\right)\cdot\left(\sum_{j=0}^{k-3}\binom{n-(R-L+1)-2}{j}\right) (L>1\land 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}\right)\cdot\left(\sum_{j=0}^{k-2}\binom{n-R-1}{j}\right) (L=1 \land R<n) \]\[ans_4=\sum_{L=2}^n\left(\sum_{i=L}^na_i10^{n-i}\right)\cdot\left(\sum_{j=0}^{k-2}\binom{L-2}{j}\right) (L>1\land 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}^m\binom{n}{i} \]

則有

\[ans_2=\sum_{i=1}^n a_i10^{n-i}=10^n\sum_{i=1}^na_i10^{-i}=10^nf(n) \]\[ans_3=\sum_{R=1}^{n-1}\left(\sum_{i=1}^Ra_i10^{R-i}\right)\cdot\left(\sum_{j=0}^{k-2}\binom{n-R-1}{j}\right)\\ =\sum_{R=1}^{n-1}10^R\left(\sum_{i=1}^Ra_i10^{-i}\right)\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}^n\left(\sum_{i=L}^na_i10^{n-i}\right)\cdot\left(\sum_{j=0}^{k-2}\binom{L-2}{j}\right)\\ =\sum_{L=2}^n10^n\left(\sum_{i=L}^na_i10^{-i}\right)\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}\right)\cdot\left(\sum_{j=0}^{k-3}\binom{n-(R-L+1)-2}{j}\right)\\ =\sum_{len=1}^{n-2}\sum_{R=len+1}^{n-1}\left(\sum_{i=R-len+1}^Ra_i10^{R-i}\right)\cdot\left(\sum_{j=0}^{k-3}\binom{n-len-2}{j}\right)\\ =\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}\right)\\ =\sum_{len=1}^{n-2}h(n-len-2,k-3)\sum_{R=len+1}^{n-1}10^R\left(\sum_{i=R-len+1}^Ra_i10^{-i}\right)\\ =\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)\right)-\left(\sum_{R=len+1}^{n-1}10^Rf(R-len)\right)\right]\\ =\sum_{len=1}^{n-2}h(n-len-2,k-3)\left[\left(\sum_{R=len+1}^{n-1}10^Rf(R)\right)-10^{len}\left(\sum_{R=len+1}^{n-1}10^{R-len}f(R-len)\right)\right]\\ =\sum_{len=1}^{n-2}h(n-len-2,k-3)\left[g(n-1)-g(len)-10^{len}g(n-1-len)\right]\\ \]

至此,如果我們已知 \(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)\)\(1\sim n-1\) 這些位置選不超過 \(m\) 個位置的方案數,也可以是 \(2\sim n\) 這些位置選不超過 \(m\) 個位置的方案數,將兩者相加,重合部分是位置 \(1\) 和位置 \(n\) 都沒有被選,且 \(2\sim n-1\) 選了不超過 \(m\) 個位置的方案數,即 \(\sum_{i=0}^m\binom{n-2}{i}\)。缺少的部分是位置 \(1\) 和位置 \(n\) 都被選了,且 \(2\sim n-1\) 選了不超過 \(m-2\) 個位置的方案數,即 \(\sum_{i=0}^{m-2}\binom{n-2}{i}\)

所以有

\[h(n,m)=2\times h(n-1,m)-\sum_{i=0}^m\binom{n-2}{i}+\sum_{i=0}^{m-2}\binom{n-2}{i}\\ =2\times h(n-1,m)-\binom{n-2}{m-1}-\binom{n-2}{m}\\ =2\times h(n-1,m)-\binom{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\n", calc());
    }
    return 0;
}