题目链接:
https://acm.hdu.edu.cn/showproblem.php?pid=7060
题目大意:
给一个长度为n的数字串()和
,表示长度为n的数字串可以被划分成1 - k个数字,求所有划分出来的数字和。
解题思路:
这题在赛场上我读错了两次题,两次都通过找规律把公式算出来了,但是题读错了。第三次读对题之后已经想不动了(读对题很重要!!!!)
其实找规律也能做,但是会很慢(可是我太笨了,不会看贡献
经过题解的指导,我悟了。
(默认首位为0)对于第i位数字s[i],当前的权值位是j,权值位为j的数字有num个,我们易得
我们不难发现需要把num[i][j]算出来
我们要分两类情况讨论
1、i + j =n
我们可以发现,还可以插入k-1个
2、i + j < n
设
(这个转换可以通过组合数的公式化简得到)
但是这个时候我们发现时间复杂度还是不行,因为又有i又有j
我们可以发现对于 的答案只与j有关
又因为对于i + j < n的情况,有很多种,因此我们考虑用前缀和计算。
具体细节在可以直接看代码
注意:对于公式里的n和n-1可能说的不是很清晰,我的代码都是从0开始,具体以代码为准,理解这个思想就好。
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 3;
const int MOD = 998244353;
int n, k, T;
char s[N];
ll p10[N];
ll fac[N], inv[N];
ll f[N][2];
ll Qpow (ll a, ll b) {
ll ans = 1ll;
while (b) {
if (b & 1)
ans = ans * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return ans;
}
void init() {
fac[0] = 1;
for (int i = 1; i <= N; i++)
fac[i] = (ll) fac[i - 1] * i % MOD;
inv[N] = Qpow (fac[N], MOD - 2);
for (int i = N - 1; i >= 0; i--)
inv[i] = (ll) inv[i + 1] * (i + 1) % MOD;
p10[0] = 1;
for (int i = 1; i <= N; i++) p10[i] = 10ll * p10[i - 1] % MOD;
}
ll C (int n, int m) {
if (m > n) return 0;
return (ll) fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
void work(){
ll k;
scanf("%lld",&k);
scanf("%s",s);
int n = strlen(s);int sum = 0;
f[0][0]= (k >= 2) ? 1 : 0;
f[0][1] = 1;
ll ans = 0;
for (int i = 1; i <= n; i++) {
f[i][0] = ( (f[i - 1][0] + f[i - 1][0]) % MOD + MOD - C (i - 1, k - 2) ) % MOD;
f[i][1] = ( (f[i - 1][1] + f[i - 1][1]) % MOD + MOD - C (i - 1, k - 1) ) % MOD;
}
for (int i = n - 1; i >= 0; i--) {
if (i < n - 1)
sum = (sum + 1ll * p10[n - 2 - i] * f[i][0] % MOD) % MOD;
ans = (ans + 1ll * (s[i] - '0') * (sum + 1ll * p10[n - 1 - i] * f[i][1] % MOD) % MOD) % MOD;
}
printf ("%d\n", ans);
}
int main() {
init();
int T;
scanf("%d",&T);
while (T--)
work();
return 0;
}