简化题面:
从集合{(1,2,3....n)}中选择(m)个互不相同的非空集合且所有元素出现次数为偶数的方案数。
解题思路:
因为对于集合位置不同的方案数算1,因此我们考虑像全排列变组合数一样对无序的方案数除以(m!)就是我们所要的答案。
第一眼看上去像是第二类斯特林数?但是仔细一看并不是。考虑是(dp[i])为选(i)个集合并且满足限制条件的有序方案数。
直接求很困难因此考虑容斥,总方案减不合法方案就是最后的答案
考虑总方案是什么,由于限制元素所有元素出现次数必须为偶数,因此对于(i)是确定的,因此总方案为(2^n-1)的集合(注意这里舍去了空集)中选取(i-1)的排列(A_{2^n-1}^{i-1})
对于不合法的方案我们考虑两种情况
1,前(i-1)个集合合法但第(i)个为空集,因此还是(dp[i-1])。
2,前(i-1)个集合合法第(i)个集合和前面相同,因此我们要将两个重复的集合都删去,即为(dp[i-2] imes (i-1) imes (2^n-1-(i-2)))
那么对于(dp[i])我们就有了递推式(dp[i]=A_{2^n-1}^{i-1}-dp[i-1]-dp[i-2] imes (i-1) imes (2^n-1-(i-2)))
最后答案除以(m!)即可
Code
#include<bits/stdc++.h>
#define LL long long
#define _ 0
using namespace std;
/*Grievous Lady*/
template <typename _n_> void mian(_n_ & _x_){
_x_ = 0;int _m_ = 1;char buf_ = getchar();
while(buf_ < '0' || buf_ > '9'){if(buf_ == '-')_m_ =- 1;buf_ = getchar();}
do{_x_ = _x_ * 10 + buf_ - '0';buf_ = getchar();}while(buf_ >= '0' && buf_ <= '9');_x_ *= _m_;
}
const int kato = 1e6 + 10;
#define mod 100000007
inline LL quick_pow(LL a , LL b){
LL res = 1;
for(; b ; b >>= 1 , a = a * a % mod){
if(b & 1){
res = res * a % mod;
}
}
return res % mod;
}
LL n , m , fac = 1;
LL A[kato] , dp[kato];
inline int Ame_(){
mian(n) , mian(m);
int poi = quick_pow(2 , n) - 1;
A[0] = 1;
dp[0] = 1 , dp[1] = 0;
for(int i = 1;i <= m;i ++){
A[i] = (A[i - 1] * ((poi - i + 1 + mod) % mod)) % mod;
}
for(int i = 2;i <= m;i ++){
dp[i] = (A[i - 1] - dp[i - 1] - dp[i - 2] * (i - 1) % mod * (poi - i + 2 + mod) % mod + mod) % mod;
}
for(int i = 2;i <= m;i ++){
fac = fac * i % mod;
}
printf("%lld
" , dp[m] * quick_pow(fac , mod - 2) % mod);
return ~~(0^_^0);
}
int Ame__ = Ame_();
int main(){;}
-----(egin{aligned}mathcal{Ame}end{aligned})__