[组合数学] 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}\),将四种情况相加,即得答案。
考虑怎么加速计算,不妨设
则有
至此,如果我们已知 \(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}\)。
所以有
所以我们同样可以 \(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;
}