1. 程式人生 > 其它 >Solution -「CF520E」Pluses everywhere

Solution -「CF520E」Pluses everywhere

$${\Large \mathbb{No \ hay \ cosa \ mas \ feliz \ en \ el \ mundo \ que \ ver \ tu \ sonrisa \ mi \ Miffy}}$$
\[{\Large \mathbb{No \ hay \ cosa \ mas \ feliz \ en \ el \ mundo \ que \ ver \ tu \ sonrisa \ mi \ Miffy}} \]

Step 1.

轉化一步題目:考慮有 \(n\) 個小球,每個小球有 \(a_i\) 的價值,\(m\) 個板子,把板子插進小球間的空隙,且不能插在第 \(1\) 個球之前與第 \(n\) 個球之後。

且規定對於任意一個方案,若第 \(i\) 個球與下一塊板子間相隔 \(j\) 個球,則該球對答案的貢獻為 \(10^j a_i\)。現需要求出每一個方案每一個球的貢獻和。

這題顯然和原題是等價的(轉化可能是因為它更組合一些 www),因為對於任意一個方案中的任意一個數字來說,只有它和下一個加號之間的數的個數

會影響它對總答案的貢獻。

於是我們考慮上面那個問題的答案。

對於第 \(i\) 個球,我們列舉 \(j\),並嘗試求出會有多少方案使得目前的狀態成立。可以發現 \(0 \leq j \leq n - i\)

可以明確第 \(i\) 個球后的 \(j\) 個球之間都不能再插板,且第 \(j\) 個球和第 \(j + 1\) 個球之間一定需要插板、總共有 \(n - 1\) 個位置可以插板。那麼也就是說總方案應該是在剩下的 \(n - 1 - j - 1\) 個空位中插 \(m - 1\) 個板的方案數,即 \(\dbinom {n - j - 2} {m - 1}\)

特殊的,如果 \(j = n - i\)

,則柿子會有所改變。因為第 \(n\) 個球后不能插板,所以說可以轉化為在 \(i\) 以前的能插的位置中插入 \(m\) 個板的方案,即 \(\dbinom {i - 1} {m}\)

顯然當前狀態的第 \(i\) 個球的貢獻應是 \(10^j a_i\),故總答案為 \({\large \sum \limits _{i = 1}^{n}} {\large \sum \limits _{j = 0}^{n - i - 1}} \dbinom {n - j - 2} {m - 1} 10^j a_i + {\large \sum \limits _{i = 1}^{n}} \dbinom {i - 1} {m} 10^{n - i} a_i\)

這是 \(O(n^2)\) 的求解過程,不能通過,於是考慮優化。


Step 2.

你會發現,如果我們,確定了 \(j\),則我們也可以確定有多少 \(i\),可以得到該 \(j\) 的貢獻,具體來講,對於一個 \(j\),所有 \(1 \leq i \leq n - j\),都能得到 \(j\) 的貢獻,因為這些球的後面都有 \(j\) 個球可以用於隔開自己和下一個加號。

當然這裡仍需要考慮當前球后面都不再放加號的特殊情況,仍是單拎出來計算。

於是就可以直接來列舉 \(j\)。記 \(S(x) = {\large \sum \limits _{i = 1}^{x}} a_i\),則答案可寫為 \({\large \sum \limits _{j = 0}^{n - 1}} 10^jS(n - j - 1)\dbinom {n - i - 2} {m - 1} + {\large \sum \limits _{i = 1}^{n}} \dbinom {i - 1} {m} 10^{n - i} a_i\)

提前初始化組合數、\(10\) 的次方都是基本功了嘛。


Code.

#include <cstdio>

typedef long long LL;
int Abs(int x) { return x < 0 ? -x : x; }
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }

int read() {
    int x = 0, k = 1;
    char s = getchar();
    while(s < '0' || s > '9') {
        if(s == '-')
            k = -1;
        s = getchar();
    } 
    while('0' <= s && s <= '9') {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * k;
}

void write(int x) {
    if(x < 0) {
        x = -x;
        putchar('-');
    }
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

void print(int x, char s) {
    write(x);
    putchar(s);
}

const int mod = 1e9 + 7;
const int MAXN = 1e5 + 5;

char s[MAXN];
int fac[MAXN], inv[MAXN], q[MAXN], p[MAXN], sum[MAXN];

inline int Sub(int x) { return x < 0 ? x + mod : x; }
inline int Plus(int x) { return x >= mod ? x - mod : x; }
inline int C(int n, int m) { 
    if(n < m)
        return 0;
    return (LL)fac[n] * inv[m] % mod * inv[n - m] % mod; 
}

void init() {
    inv[1] = 1;
    for(int i = 2; i < MAXN; i++)
        inv[i] = (LL)(mod - mod / i) * inv[mod % i] % mod;
    fac[0] = 1, inv[0] = 1, p[0] = 1;
    for(int i = 1; i < MAXN; i++) {
        p[i] = (LL)p[i - 1] * 10 % mod;
        fac[i] = (LL)fac[i - 1] * i % mod;
        inv[i] = (LL)inv[i - 1] * inv[i] % mod;
    }
}

int main() {
    init();
    int n = read(), m = read();
    scanf ("%s", s + 1);
    for(int i = 1; i <= n; i++) {
        q[i] = s[i] - '0';     
        sum[i] = Plus(sum[i - 1] + q[i]);   
    }
    int ans = 0;
    for(int i = 0; i < n; i++)
        ans = Plus(ans + (LL)sum[n - i - 1] * p[i] % mod * C(n - i - 2, m - 1) % mod);
    for(int i = 1; i <= n; i++)
        ans = Plus(ans + (LL)q[i] * p[n - i] % mod * C(i - 1, m) % mod);
    print(ans, '\n');
    return 0;
}