题解 P3214 【[HNOI2011]卡农】

简化题面:

从集合{(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})__

原文地址:https://www.cnblogs.com/Ame-sora/p/13639652.html