[CF520E] Pluses everywhere

Pluses everywhere

题意:
给你一个 n n n位数 a a a,让你在这些数之间添加 k k k + + +号,然后得到一个合法表达式,求所有可能的不同的合法表达式之和,答案对 1 0 9 + 7 10^9 + 7 109+7取模。

分析:
题意很简单,但要怎么去想呢?这可以说是一道计数dp的题吧。
我们可以单独的考虑每一位 a i a_i ai的贡献,首先可以肯定的是,一共有 n − 1 n - 1 n1个位置可以放 + + +号,然后我们根据离 a i a_i ai最近且在 a i a_i ai之后的 + + +号位置进行讨论:

  1. 最近的一个加号在 a i a_i ai后面,也就是说 a i a_i ai作为个位,那么剩下的 k − 1 k - 1 k1个加号可以有 n − 2 n - 2 n2个位置填,其贡献为: a i × C n − 2 k − 1 a_i \times C_{n - 2}^{k - 1} ai×Cn2k1
  2. 最近的一个加号在 a i + 1 a_{i + 1} ai+1后面,也就是说 a i + 1 a_{i + 1} ai+1作为十位,那么剩下的 k − 1 k - 1 k1个加号可以有 n − 3 n - 3 n3个位置填,其贡献为: a i × 10 × C n − 3 k − 1 a_i \times 10 \times C_{n - 3}^{k - 1} ai×10×Cn3k1
  3. 依次类推,最近的一个加号在 a i + n − k − 1 a_{i + n - k - 1} ai+nk1后面,那么剩下的 k − 1 k - 1 k1个加号可以有 n − 2 − ( n − k − 1 ) = = k − 1 n - 2 - (n - k - 1) == k - 1 n2(nk1)==k1个位置填,其贡献为: a i × 1 0 n − k − 1 × C k − 1 k − 1 a_i \times 10^{n - k - 1} \times C_{k - 1}^{k - 1} ai×10nk1×Ck1k1
  4. 然后假如我们距离 a i a_i ai最近的一个加号在 a n a_n an后面,因为 a n a_n an后面不能放加号,所以我们还剩下 k k k个加号, i − 1 i - 1 i1个位置可以放,其贡献为: a i × 1 0 n − i × C i − 1 k a_i \times 10^{n - i} \times C_{i - 1}^{k} ai×10ni×Ci1k

那么我们的答案就是这些贡献总和。
化简下可以得到一个最终的公式: ∑ i = 1 n − k 1 0 i − 1 × ( ∑ j = 1 n − i a j × C n − 1 − i k − 1 + a n − i + 1 × C n − i k ) \large \sum_{i = 1}^{n - k}10^{i - 1} \times (\sum_{j = 1}^{n - i}a_j \times C_{n - 1 - i}^{k - 1} + a_{n - i + 1} \times C_{n - i}^{k}) i=1nk10i1×(j=1niaj×Cn1ik1+ani+1×Cnik)

代码如下:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int maxn = 1e5 + 5;

ll qpow(ll a, ll b, ll mod) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll fac[maxn], inv[maxn];

void init(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
    inv[n] = qpow(fac[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
}

ll C(int n, int m) {
    if (n < m) return 0;
    return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int a[maxn];
ll dp[maxn], pre[maxn], res, cur = 1ll;

int main() {
    int n, k;
    char ch;
    scanf("%d%d", &n, &k);
    getchar();
    for (int i = 1; i <= n; i++) {
        scanf("%c", &ch);
        a[i] = ch - '0';
        pre[i] = pre[i - 1] + a[i];
    }
    init(n);
    for (int i = 1; i <= n - k; i++) {
        res = (res + cur * (pre[n - i] * C(n - i - 1, k - 1) % MOD +
                    a[n - i + 1] * C(n - i, k) % MOD) % MOD) % MOD;
        cout << pre[n-i]*C(n-i-1,k-1) << " " << a[n-i+1]*C(n-i,k) << endl;
        cur = cur * 10 % MOD;
    }

    printf("%lld\n", res);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值