HNOI2011 卡农(组合数学)(DP)(容斥)

传送门

我们先不考虑重排,最后除一个 m ! m! 就可以了

题意:值域在 [ 1 , n ] [1,n] 间,选择 m m 个不同的集合,使得每一个元素的出现次数为偶数
n , m 1 e 6 n,m\le 1e6

假设要选择 k k 个集合,如果我们定下来 k 1 k-1 个,那么为了满足每一个元素都是偶数的限制,最后一个集合是确定的
大力 d p dp ,令 f i f_i 表示选择 i i 个集合且合法的方案数,容斥转移
需要减去前 k 1 k-1 个集合已经满足最后一个为空,以及最后一个钦定的与前面某一个集合相同的方案数
第一个就是 f i 1 f_{i-1} ,第二个枚举重复的位置
注意集合间是有序的

f i = A 2 n 1 i 1 f i 1 f i 2 ( 2 n 1 ( i 2 ) ) ( i 1 ) f_i=A_{2^n-1}^{i-1}-f_{i-1}-f_{i-2}*(2^n-1-(i-2))*(i-1)

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 100000007;
cs int N = 1e6 + 5;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int n, m, fac[N], f[N];
int main(){
	scanf("%d%d", &n, &m);
	int up = dec(ksm(2, n),1); fac[0] = 1; int inv = 1;
	for(int i = 1; i <= m; i++) inv = mul(inv, i); inv = ksm(inv, Mod-2);
	for(int i = 1; i <= m; i++) fac[i] = mul(fac[i - 1], (up - i + 1));
	f[0] = 1;
	for(int i = 2; i <= m; i++){
		f[i] = dec(fac[i - 1], add(f[i - 1], mul(mul(i-1, dec(up,i-2)), f[i-2])));
	} cout << mul(f[m], inv); return 0;
}
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103601764