数数 题解

2000💩省选/NOI−组合数学

数数 题解

write by 小超手123

题意:

现在有四种物品,分别有 \(n_{1},n_{2},n_{3},n_{4}\) 个,有多少种排列物品的方案使得任意两个相邻物品的种类不同。

\(n_{1},n_{2} \le 200, \ \ n_{3},n_{4} \le 50000\)

分析:

可以考虑先把物品 \(A,B\) 排列好,再把物品 \(C,D\) 插入进去。

需要注意的是 \(ABB\)\(BB\) 中间必须填入 \(C,D\)

\(f_{i,j,k,0/1}\) 表示用了 \(i\) 个物品 \(A\)\(j\) 个物品 \(B\),其中相邻的且相等\(k\) 对,当前最后一个选的物品 \(A/B\)


step 1 : 分配哪些空位要填入 \(C,D\)

因此有 \(n_{1}+n_{2}+1\) 个空位,总共需用 \(C,D\) 填入 \(j\) 个空位,有 \(i\) 个空位已经强制要求必须填。\(i,j\) 均需要枚举。

那么方案数为:

\[(f_{n_{1},n_{2},i,1}+ f_{n_{1},n_{2},i,1}) \times \binom{n_{1}+n_{2}+1-i}{j-i} \]

step 2 : 把 \(C,D\) 填入空位

可以发现中间空位填的 \(C,D\) 只有三种情况:

  • 类型一:形如 \(CDCDC\)。相当于先放一个 \(C\) 进去,然后放若干个 \([DC]\)。即 \(C\) 的个数比 \(D\) 的个数多 \(1\)。记这样的有 \(X\) 种。
  • 类型二:形如 \(DCDCD\)。相当于先放一个 \(D\) 进去,然后放若干个 \([CD]\)。即 \(D\) 的个数比 \(C\) 的个数多 \(1\)。记这样的有 \(Y\) 种。
  • 类型三:形如 \(DCDC\)\(CDCD\)。相当于直接放若干个 \([CD]\)\([DC]\)。即 \(D\) 的个数与 \(C\) 的个数相等。记这样的有 \(Z\) 种。

不难发现 \(n_{3}-n_{4}=X-Y,Z=j-X-Y\)。因此只需要再枚举 \(X\) 即可。

所以可以给每个要填空位分配一种类型:\(\binom{j}{X} \times \binom{j-X}{Y}\)

即上文所述 \([CD],[DC]\)\(C,D\) 一定成对出现) 的总个数为 \(num\)。显然 \(num = \frac{n_{3}+n_{4}-X-Y}{2}\)

将这 \(num\)\([CD],[DC]\) 放进 \(j\) 个空位里。需要注意的是对于类型三的空位,至少要放一个 \([CD]\)\([DC]\)

\(num\) 减掉 \(Z\),然后插板即可。因此有 \(\binom{num-Z+j-1}{j-1}\) 种情况。

由于类型三有两种小情况,因此还要乘上 \(2^{Z}\)


乘法原理,需要枚举 \(i,j,X\)

最后答案就是:

\[(f_{n_{1},n_{2},i,0}+ f_{n_{1},n_{2},i,1}) \times \binom{n_{1}+n_{2}+1-i}{j-i} \times \binom{j}{X} \times \binom{j-X}{Y} \times \binom{num-Z+j-1}{j-1} \times 2^{Z} \]

时间复杂度 \(O((n_{1}+n_{2})^3)\)

代码:

#include<bits/stdc++.h>
#define int long long
#define N 100000
#define mod 1000000007
using namespace std;

int Pow(int a, int n) {
	if(n == 0) return 1;
	if(n == 1) return a;
	int x = Pow(a, n / 2);
	if(n % 2 == 0) return x * x % mod;
	else return x * x % mod * a % mod;
}

int inv(int x) {
	return Pow(x, mod - 2);
}
int Inv[N + 10], fac[N + 10], h[N + 10];

void init() {
	Inv[0] = fac[0] = h[0] = 1;
	for(int i = 1; i <= N; i++) {
		fac[i] = fac[i - 1] * i % mod;
		h[i] = h[i - 1] * 2 % mod;
	} 
	Inv[N] = inv(fac[N]);
	for(int i = N - 1; i >= 1; i--) Inv[i] = Inv[i + 1] * (i + 1) % mod;
}

int C(int n, int m) {
	if(n < m || n < 0 || m < 0) return 0;
	return fac[n] * Inv[m] % mod * Inv[n - m] % mod;
}
int f[210][210][410][2];
int n1, n2, n3, n4, ans;

void upd(int &x, int y) {
	x = (x + y) % mod;
}

void DP() {
	f[1][0][0][0] = f[0][1][0][1] = 1;
	for(int i = 0; i <= n1; i++)
	for(int j = 0; j <= n2; j++)
	for(int k = 0; k <= n1 + n2; k++) {
		upd(f[i + 1][j][k + 1][0], f[i][j][k][0]);
		upd(f[i][j + 1][k][1], f[i][j][k][0]);
		upd(f[i][j + 1][k + 1][1], f[i][j][k][1]);
		upd(f[i + 1][j][k][0], f[i][j][k][1]);
	}
}

void Sol() {
	for(int j = 0; j <= n1 + n2 + 1; j++)
	for(int i = 0; i <= min(j, n1 + n2 - 1); i++)
	for(int X = 0; X <= j; X++) {
		int Y = X - (n3 - n4);
		if(Y < 0) continue;
		int Z = j - X - Y, num = (n3 + n4 - X - Y) / 2;
		upd(ans, (f[n1][n2][i][0] + f[n1][n2][i][1]) * C(n1 + n2 + 1 - i, j - i) % mod
		* C(j, X) % mod * C(j - X, Y) % mod * C(num - Z + j - 1, j - 1) % mod * h[Z] % mod);
	}
}
signed main() {
	init();
	cin >> n1 >> n2 >> n3 >> n4;
	DP();
	Sol();
	cout << ans;
	return 0;
}
posted @   CQYC题解站  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示