卡农 -- HNOI2011 -- DP&组合

卡农 -- \(HNOI2011\)

$$luogu$$

$$HZOI$$


题意

给定一个 集合 $ A= { 1 \le x \le n | x } $ , 求出其 \(m\) 个不相同的且不为空集的子集,使得第 \(i\) 个元素的在所有选出的子集中出现的次数 \(appear_i \mod 2 = 0\)


题解

首先一个已知结论:

对于一个 \(A\) 这样的集合,他不同的子集个数为 \(2^n\)

那么这时我们减去空集,那么可选的为 \(2^n - 1\) 种。

你影响你最后的每个元素的出现次数的是第 \(m\) 次的选择。

若第 \(i\) 个数在前 \(m - 1\) 个子集中出现的次数为奇数,那么 \(i\) 在最后一个中必定出现。

反之就不会出现。

所以 前 \(m - 1\) 个怎么取值都行。

考虑此时 \(ans_m\)\(m\) 个子集满足条件的方案数。

不考虑什么奇偶,前 \(m - 1\) 个能选的总方案数为 :

\[ans_m += C^{ m - 1 }_{2 ^ n - 1} \]

第一种情况:若 \(1 \dots n\) 在前 \(m - 1\) 个子集中出现的次数均为偶数,则此时末尾为空集,显然不可取,所以:

\[ans_m -= ans_{m - 1} \]

第二种情况:若所有出现次数为奇数的数构成的集合 \(B\) 已经被选入,则:

\[ans_m -= (2 ^ n - 1 - (m - 2)) \times ans_{m - 2} \]

解释一下:

那么我们可以看做就是最后一个集合 \(=\)\(m - 1\) 个集合

那前 \(m - 2\) 个肯定是符合要求的。那肯定前 \(m - 2\) 个是不重叠的那这个重叠的集合的取值种数为 $2 ^ n - 1 - (m - 1) $

最后的处理,现在定义 \(n = 2, m = 3\)

那对于这三种选法,会算成三种,但实际是一种

\[\{1\} , \{2\} , \{1 , 2\} \]

\[\{1\} , \{1 , 2\} , \{2\} \]

\[\{2\} , \{1 , 2\} , \{1\} \]

注意对于前面 $ 2 $ 个集合的来说它的选择的顺序是没关系的。算成三种的原因只是因为最后的一个集合的抠出来的值不同。

所以再除以一个 $ m $

所以 \(DP\) 转移方程为:

\[dp_i = C ^ {i - 1} _ {2 ^ n - 1} - dp_{i - 1} - dp_{i - 2} \times ( 2 ^ n - 1 - ( m - 2 ) ) \]

\(code\)

格式化过的

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std ;
const int N = 1e6 + 1 ;
int dp[ N ], C[ N ] ;
const long long mod = 1e8 + 7 ;
namespace Combination {
int nueyong[ N ], sum_neo[ N ] ;
inline void lear_neoyong() {
    sum_neo[ 0 ] = sum_neo[ 1 ] = 1 ;
    nueyong[ 1 ] = 1 ;
    nueyong[ 0 ] = 1 ;

    // sum[ 0 ] = sum[ 1 ] = 1 ;
    for (int i = 2 ; i < N ; ++ i) {
        int p = mod ;
        int k = p / i ;
        nueyong[ i ] = (k * (p - nueyong[ p % i ])) % p ;
        sum_neo[ i ] = (nueyong[ i ] * sum_neo[ i - 1 ]) % p ;
        // sum[ i ] = ( i * sum[ i - 1 ] ) % p ;
    }
}
int Quick_Pow(int alpha, int beta) {
    int ans = 1 ;

    while (beta > 0) {
        if (beta & 1)
            ans = (ans * alpha) % mod ;

        beta >>= 1 ;
        alpha = (alpha * alpha) % mod ;
    }

    return ans ;
}
} ;
using namespace Combination ;
inline int read() {
    int x = 0, f = 1 ;
    char c = getchar() ;

    while (c < '0' || c > '9') {
        if (c == '-') {
            f = - f ;
        }

        c = getchar() ;
    }

    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0' ;
        c = getchar() ;
    }

    return x * f ;
}
int n, m, all_time, koul ;
signed main() {
    n = read() ;
    m = read() ;
    lear_neoyong() ;
    all_time = (((Quick_Pow(2, n) - 1) % mod) + mod) % mod ;
    C[ 0 ] = 1 ;
    int tot = all_time ;

    for (int i = 1 ; i <= m ; ++ i) {
        C[ i ] = (C[ i - 1 ] * ((tot + mod) % mod)) % mod ;
        tot -- ;
    }

    dp[ 2 ] = dp[ 1 ] = 0 ;

    for (int i = 3 ; i <= m ; ++ i) {
        int pri = (C[ i - 1 ] * sum_neo[ i - 1 ]) % mod ;
        dp[ i ] = ((((pri - dp[ i - 1 ]) - ((dp[ i - 2 ] * ((all_time - i + 2) % mod + mod) % mod) % mod) + mod) %
                    mod) * nueyong[ i ]) % mod ;
    }

    cout << dp[ m ] ;
}

结尾撒花 \(\color{pink}✿✿ヽ(°▽°)ノ✿\)

posted @ 2024-03-07 08:22  HANGRY_Sol&Cekas  阅读(20)  评论(1编辑  收藏  举报