一起加油吧~|

shiranui

园龄:2年7个月粉丝:17关注:12

2022-11-03 13:33阅读: 58评论: 0推荐: 1

[题解] HDU7060 Separated Number 思路整理

题目链接

HDU7060 Separated Number

题目大意

给一个 n 位数,把该数字分成 k 段,每种方案的贡献为其分割出的段的数字之和。求所有分法的贡献之和(对 998244353 取模)。

做法

枚举数位,计算每个数位的贡献。

令这个 n 位数从高到低第 i 位为 ai,那么可以枚举 ai 属于分割出的哪段数以统计贡献。

ai 被分到了 aL ~ aR 段,此时 ai 的贡献为 ai×10Ri,考虑一下情况:

  • 1<LR<n 时,该段左右两边都有其他段,所以相当于余下的 n(RL+1) 位中插入了一块隔板,方案数为 j=1k2(n(RL+1)11j1)=j=0k3(n(RL+1)2j)
  • L=1R=n 时,没有剩下的数,方案数为 1
  • L=1R=n 时(LR 不同时等于 n),那么只有一边有段,方案即为 j=1k1(n(RL+1)1j1)=j=0k2(n(RL+1)1j)

加起来即为答案。

ans1=L=2n1R=Ln1(i=LRai×10Ri)×(j=0k3(n(RL+1)2j))(L>1R<n)

ans2=i=1nai×10ni(L=1R=n)

ans3=R=1n1(i=1Rai10Ri)×(j=0k2(nR1j))(L=1R<n)

ans4=L=2n(i=Lnai10ni)×(j=0k2(L2j))(L>1R=n)

ans=ans1+ans2+ans3+ans4

不妨设:

f(n)=i=1nai10i

g(n)=i=1n10if(i)

h(n,m)=i=0m(ni)

那么:


ans1

=L=2n1R=Ln1(i=LRai10Ri)×(j=0k3(n(RL+1)2j))

=len=1n2R=len+1n1(i=Rlen+1Rai10Ri)×(j=0k3(nlen2j))

=len=1n2h(nlen2,k3)R=len+1n1(i=Rlen+1Rai10Ri)

=len=1n2h(nlen2,k3)R=len+1n110R(i=Rlen+1Rai10i)

=len=1n2h(nlen2,k3)R=len+1n110R(f(R)f(Rlen))

=len=1n2h(nlen2,k3)[(R=len+1n110Rf(R))(R=len+1n110Rf(Rlen))]

=len=1n2h(nlen2,k3)[(R=len+1n110Rf(R))10len(R=len+1n110Rlenf(Rlen))]

=len=1n2h(nlen2,k3)[g(n1)g(len)10leng(n1len)]


ans2

=i=1nai10ni=10ni=1nai10i=10nf(n)


ans3

=R=1n1(i=1Rai10Ri)×(j=0k2(nR1j))

=R=1n110R(i=1Rai10i)×h(nR1,k2)

=R=1n110Rf(R)×h(nR1,k2)


ans4

=L=2n(i=Lnai10ni)×(j=0k2(L2j))

=L=2n10n(i=Lnai10i)×h(L2,k2)

=L=2n10n(f(n)f(L1))×h(L2,k2)


不难求出 f(x)g(x),如何求 h(n,m) 呢?

由定义可以发现 h(n,m) 表示的是 n 个位置选不超过 m 个位置的方案数,考虑使用容斥,从 h(n1,m) 得到 h(n,m)

  • h(n,m)1 ~ n1 选不超过 m 个位置的方案数,也是 2 ~ n 选不超过 m 个位置的方案数。
  • 两区间相加,重叠部分为 1n 位置都不选,2 ~ n1 选不超过 m 个位置的方案数,即 i=0m(n2i)
  • 缺少部分为 1n 位置都选,2 ~ n1 选不超过 m2 个位置的方案数,即 i=0m2(n2i)

可得:

h(n,m)

=2×h(n1,m)i=0m(n2i)+i=0m2(n2i)

=2×h(n1,m)(n2m1)(n2m)

=2×h(n1,m)(n1m)

注意到答案式中仅含 h(w,k2)h(w,k3),那么可以分别 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;
}

参考资料

[组合数学] HDU 7060 Separated Number - AE酱

本文作者:shiranui

本文链接:https://www.cnblogs.com/shiranui/p/16854191.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   shiranui  阅读(58)  评论(0编辑  收藏  举报
*/
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起