ACM中的整数K拆分 (有条件限制 无条件限制 插板法 URAL-1036 HDU-6397)

45 篇文章 0 订阅
44 篇文章 0 订阅

整数的K拆分

整数K拆分示例

在程序设计竞赛中,我们会经常遇到一类整数 K K K 拆分的问题。

例如:求 N N N 个非负整数之和为 S S S 的方案数(每个数字都小于 M M M)。

对于这类问题,分为两种情况:①没有条件限制。 ②有条件限制。

没有条件限制

N = 2 , S = 4 N = 2,S = 4 N=2,S=4 时,有

1. 0 + 4 1.\quad0 + 4 1.0+4
2. 1 + 3 2.\quad1 + 3 2.1+3
3. 2 + 2 3.\quad2 + 2 3.2+2
4. 3 + 1 4.\quad3 + 1 4.3+1
5. 4 + 0 5.\quad4 + 0 5.4+0

对于没有条件限制的问题,直接使用插板法求解即可。
例如要用 n n n 个数字之和等于 s s s。不妨假设要把 s s s 个球,用 n − 1 n -1 n1 块板分为 n n n 组(可为空集)。这样子,用板隔开的球数,就是划分出的 n n n 个数。
例如 n = 4 , s = 10 n = 4,s = 10 n=4s=10 时,我们可以找到一种情况为 1 + 5 + 0 + 4 1 + 5 + 0 + 4 1+5+0+4,即:
o   ∣   o   o   o   o   o   ∣   ∣   o   o   o   o o\ |\ o\ o\ o\ o\ o\ |\ |\ o\ o\ o\ o o  o o o o o   o o o o
因此我们可以看成在 n − 1 + s n - 1 + s n1+s 个空位上,选择 n − 1 n - 1 n1 个位置来插入板,将数字隔开。即: C n − 1 + s n − 1 C_{n-1+s}^{n-1} Cn1+sn1

有条件限制

有条件限制的类型题目通常为:

例如:求 n n n 个非负整数之和为 s s s 的方案数,每个数字都小于 m m m

例如 n = 2 , s = 6 , m = 5 n = 2,s = 6,m = 5 n=2s=6m=5

1. 2 + 4 1.\quad2+4 1.2+4
2. 3 + 3 2.\quad3+3 2.3+3
3. 4 + 2 3.\quad4+2 3.4+2

对于这种情况,我们也可以使用插板法 + 容斥原理。
首先要知道,我们使用容斥原理的步骤是:先把没有限制的情况算出来 − - 不符合条件的情况。
上面已经讲到,没有限制条件的情况总数为 C n − 1 + s n − 1 C_{n-1+s}^{n-1} Cn1+sn1

而我们接下来只需要算不符合条件的情况,即存在至少一个数字不小于给定的 m m m
显然,计算不符合条件的情况总数是 至少一个位置不符合条件的总数 减去 至少两个位置不符合条件的总数 加上 至少三个位置符合条件的种数……

那怎么计算至少 i i i 个位置不符合条件的总数呢?
假设题目为: 求 n n n 个非负整数之和为 s s s 的方案数,每个数字都小于 m m m

我们想计算出不符合条件的情况,我们可以考虑构造出不符合条件的情况。
如果我们取出 i ∗ m i*m im 个小球出来,再对 n − 1 + s − i ∗ m n-1+s-i*m n1+sim 个小球使用一样的插板法。即得 C n − 1 + s − i ∗ m n − 1 C_{n-1+s-i*m}^{n-1} Cn1+simn1
然后我们再将这取出来的 i ∗ m i*m im 个小球分成 i i i 组,每组 m m m 个。在上述插完板后的 n n n 个空隙中,选择 i i i 个不同的位置,把 i i i 组小球塞回到插板中。

如此一来,就可以构造出 i i i 个数字不符合条件的情况。即:
C n − 1 + s − i ∗ m n − 1 C n i C_{n-1+s-i*m}^{n-1} C_{n}^{i} Cn1+simn1Cni
但是还需要容斥,即:
∑ i = 0 n ( − 1 ) i C n − 1 + s − i ∗ m n − 1 C n i \sum_{i=0}^{n}(-1)^{i}C_{n-1+s-i*m}^{n-1} C_{n}^{i} i=0n(1)iCn1+simn1Cni

Lucky Tickets URAL - 1036

题意

给定一个 N N N 和一个 S S S ,存在某些数字长度为 2 N 2N 2N 且前后两部分的和相等,而且要求整个数字的和为 S S S ,问你这样数字的个数。

题解

可以发现,我们只需要找到 N N N 个数字和为 S 2 \frac{S}{2} 2S 的数字个数,然后平方一下就是答案所求的方案数。
不难发现还有一个要求是每一位数字都是 0 0 0 9 9 9 的。

于是我们可以得到这么一个式子:
S = ∑ i = 0 n ( − 1 ) i C S 2 + n − 1 − 10 ∗ i n − 1 C n i S = \sum_{i=0}^{n}(-1)^iC_{\frac{S}{2}+n-1-10*i}^{n-1}C_{n}^{i} S=i=0n(1)iC2S+n110in1Cni
由于数字较大,所以使用了 JAVA 的大数

import java.util.*;
import java.math.*;

public class Main {

	static BigInteger C(int n, int m) {
		if (m > n) return BigInteger.ZERO;
		BigInteger ans = BigInteger.ONE;
		for (int i = 0; i < m; i++) {
			ans = ans.multiply(BigInteger.valueOf(n - i));
			ans = ans.divide(BigInteger.valueOf(i + 1));
		}
		return ans;
	}

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int n = cin.nextInt(), s = cin.nextInt();
		if (s%2 == 1) {
			System.out.println("0");
			return;
		}
		BigInteger ans = BigInteger.ZERO;
		for (int i = 0; i <= n; i++) {
			BigInteger delta = C(n - 1 + s/2 - i*10, n - 1).multiply(C(n, i));
			ans = ans.add(BigInteger.valueOf((long)Math.pow(-1, i%2)).multiply(delta));
		}
		System.out.println(ans.multiply(ans));
	}

}

Character Encoding HDU - 6397

题意

求把 k k k 分解为 m m m 部分,且每部分都小于 n n n 的方案数( m o d   998244353 mod\ 998244353 mod 998244353 )。

题解

这道题也是普通的整数 k k k 拆分。只不过加上了 m o d 998244353 mod 998244353 mod998244353 而已。
由于答案要膜一个数,所以就把组合数用逆元求就行了。
S = ∑ i = 0 m ( − 1 ) i C k + m − 1 − n ∗ i m − 1 C m i   ( m o d   998244353 ) S = \sum_{i=0}^{m}(-1)^iC_{k+m-1-n*i}^{m-1}C_{m}^{i}\ (mod\ 998244353) S=i=0m(1)iCk+m1nim1Cmi (mod 998244353)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 998244353;
const int maxn = 3e5 + 10;
LL fact[maxn], inv[maxn], T, n, m, k, ans;

LL power(LL a, int x) {
  LL ans = 1;
  while (x) {
    if (x & 1) ans = (ans * a) % mod;
    a = (a * a) % mod;
    x >>= 1;
  }
  return ans;
}

void init() {
  fact[0] = 1;
  for (int i = 1; i < maxn; i++) {
    fact[i] = fact[i - 1] * i % mod;
  }
  inv[maxn - 1] = power(fact[maxn - 1], mod - 2);
  for (int i = maxn - 2; i >= 0; i--) {
    inv[i] = inv[i + 1] * (i + 1) % mod;
  }
}

LL C(LL n, LL m) {
  if (m > n) return 0;
  return (fact[n] * inv[n - m]) %mod * inv[m] % mod;
}


int main()
{
  init();
  scanf("%lld", &T);
  while (T--) {
    scanf("%lld %lld %lld", &n, &m, &k);
    ans = C(k + m - 1, m - 1);
    for (int i = 1; i <= m; i++) {
      ans = (ans + (LL)pow(-1, i%2) * (C(k + m - 1 - n * i, m - 1) * C(m, i))%mod + mod)%mod;
    }
    printf("%lld\n", ans %mod);
  }

  return 0;
}

后记

其实还有一道题是每一个位置都有不同条件限制,暂时没有找到那道题。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值