【2021杭电多校第八场】1005 Separated Number(思维)

题目链接:

https://acm.hdu.edu.cn/showproblem.php?pid=7060

题目大意:

给一个长度为n的数字串(n <= 10^{6})和(k <= n),表示长度为n的数字串可以被划分成1 - k个数字,求所有划分出来的数字和。

解题思路:

这题在赛场上我读错了两次题,两次都通过找规律把公式算出来了,但是题读错了。第三次读对题之后已经想不动了(读对题很重要!!!!)

其实找规律也能做,但是会很慢(可是我太笨了,不会看贡献

经过题解的指导,我悟了。

(默认首位为0)对于第i位数字s[i],当前的权值位是j,权值位为j的数字有num个,我们易得

ans = \sum_{i=0}^{n-1}\sum_{j=0}^{n-1-i}s[i] * 10^{j} *num[j]

我们不难发现需要把num[i][j]算出来

我们要分两类情况讨论

1、i + j =n

我们可以发现,还可以插入k-1个

num[i][j]=\sum_{m=0}^{k-1}\binom{n-j-1}{m}

2、i + j < n

num[i][j]=\sum_{m=0}^{k-2}\binom{n-j-2}{m}

F(a,b) = \sum_{m=0}^{b}\binom{a}{m} = 2 * F(a-1,b)-\binom{a-1}{b}

(这个转换可以通过组合数的公式化简得到)

\binom{m}{n}=\binom{m-1}{n}+\binom{m-1}{n-1}

但是这个时候我们发现时间复杂度还是不行,因为又有i又有j

我们可以发现对于10^{j}*num[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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值