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

整数的K拆分

整数K拆分示例

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

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

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

没有条件限制

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

1. 0 + 4 1.\quad0 + 4
2. 1 + 3 2.\quad1 + 3
3. 2 + 2 3.\quad2 + 2
4. 3 + 1 4.\quad3 + 1
5. 4 + 0 5.\quad4 + 0

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

有条件限制

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

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

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

1. 2 + 4 1.\quad2+4
2. 3 + 3 2.\quad3+3
3. 4 + 2 3.\quad4+2

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

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

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

我们想计算出不符合条件的情况,我们可以考虑构造出不符合条件的情况。
如果我们取出 i m i*m 个小球出来,再对 n 1 + s i m n-1+s-i*m 个小球使用一样的插板法。即得 C n 1 + s i m n 1 C_{n-1+s-i*m}^{n-1}
然后我们再将这取出来的 i m i*m 个小球分成 i i 组,每组 m m 个。在上述插完板后的 n n 个空隙中,选择 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}
但是还需要容斥,即:
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}

Lucky Tickets URAL - 1036

题意

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

题解

可以发现,我们只需要找到 N N 个数字和为 S 2 \frac{S}{2} 的数字个数,然后平方一下就是答案所求的方案数。
不难发现还有一个要求是每一位数字都是 0 0 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}
由于数字较大,所以使用了 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 分解为 m m 部分,且每部分都小于 n n 的方案数( m o d   998244353 mod\ 998244353 )。

题解

这道题也是普通的整数 k k 拆分。只不过加上了 m o d 998244353 mod 998244353 而已。
由于答案要膜一个数,所以就把组合数用逆元求就行了。
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)

#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;
}

后记

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

猜你喜欢

转载自blog.csdn.net/qq_40861916/article/details/82860726
今日推荐