Pluses everywhere


题意

??????传送门

题解

做法1

我们先考虑一种简单的做法:

我们枚举每一个区间 \([l, r]\) , 考虑如果这个区间作为一个数出现在结果中时, 可以发现, 此时的贡献即为 \(l\) 的左边和 \(r\) 的右边都各用一个 \(+\) , 剩下的 \(+\) 在此区间外乱选, 可推出答案的表达式:

\[\Large \sum \limits_{r = 1}^{n} \sum \limits_{l = 1}^{r}S_{[l, r]} \times \dbinom{n - (r - l) - 3}{m - 2} \]

这里需要注意一点, \(0\)\(+\)\(1\)\(+\) 时需要特判, 当右端点在末尾时要特判。

做法2

考虑移动 \([l, r]\) 的左端加号时, \([l, r]\) 区间的每一个数对答案的贡献不变, 而移动右端加号时 \([l, t]\) 区间的每一个数对答案的贡献都要乘以 \(10\)

故此, 我们对于每一个数, 都只考虑其右边最近的一个 \(+\) 的位置是什么就足够了。

对于每个数, 当它的位置为 \(l\) , 右侧最近的 \(+\) 位置为 \(r\) 时, 其表示的值为 \(\large s_r \times 10^{(r - l)}\) , 被访问的次数为 \(\large \dbinom{n - (r - l) - 2}{m - 1}\) 。 所以, 我们可以写出答案的表达式:

\[\Large s_r \sum \limits_{k = 0}^{n - r} 10^{k}\dbinom{n - 2 - k}{m - 1} \]

这里需要注意一点, \(0\)\(+\) 时需要特判, 当端点在末尾时要特判。

做法三

分析上式, 我们发现对于每一个右侧 \(+\) 与其距离相同的情况(除末尾), 被访问的次数是相同的。

故此, 我们可以将所有的与右侧加号距离相同的情况所表示的数给累加起来, 最后再乘以它的出现次数即可。

代码:

#include 
#include 
using namespace std;

#define MAXN 100000
#define INF 1000000007

long long s[MAXN + 5];
long long pr[MAXN + 5];
long long fac[MAXN + 5], inv_fac[MAXN + 5];

long long Pow (long long a, int b) {
	long long ans = 1;
	
	while (b) {
		if (b & 1) {
			ans *= a;
			ans %= INF;
		}
		a *= a;
		a %= INF;
		b >>= 1;
	} 
	
	return ans;
}
long long C(int n, int m) {
	if (m > n) {
		return 0;
	}
	
	return (fac[n] * inv_fac[m] % INF * inv_fac[n - m] % INF);
}

int main () {
	int n, m;
	
	scanf ("%d %d", &n, &m);
    fac[0] = 1;
    for (int i = 1; i <= n; i ++) {
    	fac[i] = fac[i - 1] * i % INF;
	}
	inv_fac[n] = Pow (fac[n], INF - 2);
	for (int i = n - 1; i >= 0; i --) {
		inv_fac[i] = inv_fac[i + 1] * (i + 1) % INF;
	}
	for (int i = 1; i <= n; i ++) {
		scanf ("%1lld", &s[i]);
		pr[n - i + 1] = s[i];
	}
	for (int i = n; i >= 1; i --) {
		pr[i] += pr[i + 1];
	}

	long long pow10 = 1;
	long long ans = 0;

	for (int i = 1; i <= n; i ++) {
		pr[i] -= s[n - i + 1];
		s[n - i + 1] *= pow10;
		s[n - i + 1] %= INF;
		pr[i] += INF;
		pr[i] %= INF;
		pr[i] *= pow10;
		pr[i] %= INF;
		pow10 = ((pow10 << 1) + (pow10 << 3)) % INF;
	}
	for (int i = 1; i <= n; i ++) {
		if (i < n && m) {
			ans += pr[i] * C(n - i - 1, m - 1) % INF;
			ans %= INF;
		}
		ans += s[n - i + 1] * C(n - i, m) % INF;
		ans %= INF;
	}
	printf ("%lld", ans);
	
	return 0;
}