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

本文探讨了一个关于集合选择的问题,利用组合数学与动态规划解决元素出现次数为偶数的约束条件下的方案计数问题。通过容斥原理进行状态转移,避免了重复计数的情况。

传送门

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

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

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

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) fi=A2n1i1fi1fi2(2n1(i2))(i1)

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值