[题解] HDU7060 Separated Number 思路整理
题目链接
题目大意
给一个 \(n\) 位数,把该数字分成 \(k\) 段,每种方案的贡献为其分割出的段的数字之和。求所有分法的贡献之和(对 \(998244353\) 取模)。
做法
枚举数位,计算每个数位的贡献。
令这个 \(n\) 位数从高到低第 \(i\) 位为 \(a_i\),那么可以枚举 \(a_i\) 属于分割出的哪段数以统计贡献。
设 \(a_i\) 被分到了 \(a_L\) ~ \(a_R\) 段,此时 \(a_i\) 的贡献为 \(a_i\times10^{R-i}\),考虑一下情况:
- 当 \(1<L\) 且 \(R<n\) 时,该段左右两边都有其他段,所以相当于余下的 \(n-(R-L+1)\) 位中插入了一块隔板,方案数为 \(\sum_{j=1}^{k-2}\binom{n-(R-L+1)-1-1}{j-1}=\sum_{j=0}^{k-3}\binom{n-(R-L+1)-2}{j}\)。
- 当 \(L=1\) 且 \(R=n\) 时,没有剩下的数,方案数为 \(1\)。
- 当 \(L=1\) 或 \(R=n\) 时(\(L\)、\(R\) 不同时等于 \(n\)),那么只有一边有段,方案即为 \(\sum_{j=1}^{k-1}\binom{n-(R-L+1)-1}{j-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_i\times10^{R-i}\right)\times\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_i\times10^{n-i} (L=1 \land R=n)\)
\(ans_3=\sum_{R=1}^{n-1}\left(\sum_{i=1}^Ra_i10^{R-i}\right)\times\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)\times\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_1\)
\(=\sum_{L=2}^{n-1}\sum_{R=L}^{n-1}\left(\sum_{i=L}^Ra_i10^{R-i}\right)\times\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)\times\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]\)
\(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)\times\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)\times h(n-R-1,k-2)\)
\(=\sum_{R=1}^{n-1}10^Rf(R)\times h(n-R-1,k-2)\)
\(ans_4\)
\(=\sum_{L=2}^n\left(\sum_{i=L}^na_i10^{n-i}\right)\times\left(\sum_{j=0}^{k-2}\binom{L-2}{j}\right)\)
\(=\sum_{L=2}^n10^n\left(\sum_{i=L}^na_i10^{-i}\right)\times h(L-2,k-2)\)
\(=\sum_{L=2}^n10^n(f(n)-f(L-1))\times h(L-2,k-2)\)
不难求出 \(f(x)\) 和 \(g(x)\),如何求 \(h(n,m)\) 呢?
由定义可以发现 \(h(n,m)\) 表示的是 \(n\) 个位置选不超过 \(m\) 个位置的方案数,考虑使用容斥,从 \(h(n-1,m)\) 得到 \(h(n,m)\)。
- \(h(n,m)\) 是 \(1\) ~ \(n-1\) 选不超过 \(m\) 个位置的方案数,也是 \(2\) ~ \(n\) 选不超过 \(m\) 个位置的方案数。
- 两区间相加,重叠部分为 \(1\)、\(n\) 位置都不选,\(2\) ~ \(n-1\) 选不超过 \(m\) 个位置的方案数,即 \(\sum_{i=0}^m\binom{n-2}{i}\)。
- 缺少部分为 \(1\)、\(n\) 位置都选,\(2\) ~ \(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}\)
注意到答案式中仅含 \(h(w,k-2)\) 和 \(h(w,k-3)\),那么可以分别 \(O(n)\) 求出。
\(f(n)\) 和 \(g(n)\) 也可 \(O(n)\) 处理,再 \(O(n)\) 计算答案,故总复杂度 \(O(n)\)。
代码
const ll mod = 998244353;
const int N = 1000010, M = 1000000;
int t, n, k, a[N];
char c[N];
ll inv[N], fac[N], finv[N], ten[N], f[N], g[N], h1[N], h2[N];
inline ll C(ll n, ll m) {
if (m > n) return 0;
if (m == n || n == 0) return 1;
return fac[n] * finv[m] % mod * finv[n - m] % mod;
}
inline ll calc() {
ll res = ten[n] * f[n] % mod;
if (k == 1) return res;
for (int R = 1; R <= n - 1; R++)
res = (res + ten[R] * f[R] % mod * h1[n - R - 1] % mod) % mod;
for (int L = 2; L <= n; L++)
res = (res + ten[n] * ((f[n] + mod - f[L - 1]) % mod) % mod * h1[L - 2]) % mod;
if (k == 2) return res;
for (int len = 1; len <= n - 2; len++)
res = (res + h2[n - len - 2] * (((g[n - 1] + mod - g[len]) % mod + mod - ten[len] * g[n - 1 - len] % mod) % mod) % mod) % mod;
return res;
}
int main() {
fac[0] = fac[1] = 1;
for (int i = 2; i <= M; i++) fac[i] = fac[i - 1] * i % mod;
inv[1] = 1;
for (int i = 2; i <= M; i++) inv[i] = (mod - (mod / i) * inv[mod % i]) % mod;
finv[0] = finv[1] = 1;
for (int i = 2; i <= M; i++) finv[i] = finv[i - 1] * inv[i] % mod;
ten[0] = 1;
for (int i = 1; i <= M; i++) ten[i] = ten[i - 1] * 10 % mod;
t = read();
while (t--) {
k = read();
scanf("%s", c + 1);
n = strlen(c + 1);
for (int i = 1; i <= n; i++) a[i] = c[i] ^ 48;
for (int i = 1; i <= n; i++) h1[i] = h2[i] = 0;
ll iv10 = inv[10];
for (int i = 1; i <= n; i++) {
f[i] = (f[i - 1] + iv10 * a[i] % mod) % mod;
iv10 = iv10 * inv[10] % mod;
}
for (int i = 1; i <= n; i++) g[i] = (g[i - 1] + f[i] * ten[i] % mod) % mod;
h1[0] = h2[0] = 1;
if (k >= 2)
for (int i = 1; i <= n; i++) h1[i] = ((h1[i - 1] << 1) % mod + mod - C(i - 1, k - 2)) % mod;
if (k >= 3)
for (int i = 1; i <= n; i++) h2[i] = ((h2[i - 1] << 1) % mod + mod - C(i - 1, k - 3)) % mod;
printf("%lld\n", calc());
}
return 0;
}