[luogu p3214] [HNOI2011] 卡农

Link\mathtt{Link}

P3214 [HNOI2011] 卡农 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description\mathtt{Description}

在集合 S=1,2,,nS = {1, 2, \ldots, n} 中,选出 mm 个子集,使得

  • 无空集
  • 选定的 mm 个集合两两不同
  • 选定的 mm 个集合中,11nn 中每个元素出现次数必为偶数。

求选定方案数。答案对 108+710^8+7 取模。

Data Range & Restrictions\mathtt{Data} \text{ } \mathtt{Range} \text{ } \mathtt{\&} \text{ } \mathtt{Restrictions}

  • 1n,m1061 \le n, m\le 10^6

Solution\mathtt{Solution}

为什么不 % 1e9 + 7??为什么为什么为什么

本题最棘手的部分是第三个性质。

那么先不考虑第三个性质,只考虑前两个,容易发现一共可以有 2n12 ^n - 1 个非空集合可选,选 mm 个。显然答案是 (2n1m)\dbinom{2^n - 1}{m}

再考虑第三个性质,发现在这种情况下,如果选定了 m1m - 1 个集合已经确定,第 mm 个集合实质上已经确定。因为在前 m1m - 1 个集合中出现了奇数次的元素一定要在这第 mm 个集合中,出现了偶数次的元素一定不能在这第 mm 个集合中,否则便不满足元素出现次数为偶数。

那么答案就是简单的 (2n1m1)\dbinom{2^n - 1}{m - 1} 吗?很遗憾的发现,第三个性质成功维护,而第一个和第二个性质没维护上。也就是说:如果新的集合是空集,或者新的集合与前几个集合重复,这些情况还需要处理。

分别处理。

首先对于新的集合是空集的情况,其实就是不算新集合,前面 m1m - 1 个集合满足元素次数出现偶数次的情况。这提示我们递推:如果设 fif_iii 个集合中满足选法的方案数,事实上它是跟 fi1f_{i - 1} 有关的,即新的集合是空集的情况有 fi1f_{i - 1} 种。

顺着这个继续考虑重复。如果目前要选的第 mm 个集合与前 m1m - 1 个集合中有一对重复的,考虑到去掉这两个集合后,剩下的 m2m -2 个集合正好有 fi2f_{i - 2} 种方案。(因为原先是满足第三条性质的,去掉两个相同的集合,显然元素出现次数仍然都是偶数,这个性质仍然在维护)。而这两个集合则可以有 2n1(i2)2^n - 1 - (i - 2) 种选法,所以新的集合与原来有所重复的情况一共有 fi2×(2ni+1)f_{i - 2} \times (2^n - i + 1) 种。

注意到,式子中的组合数事实上只限制了 i1i - 1 个元素的无序性,也就是说每种方案还是重复了 ii 次。我们需要再除以 ii.

总递推式:

fi=(2n1i1)fi1fi2×(2ni+1)if_i = \dfrac{\dbinom{2^n - 1}{i - 1} - f_{i - 1} - f_{i - 2} \times (2^n - i +1)}{i}

Time Complexity\mathtt{Time} \text{ } \mathtt{Complexity}

O(nlogn)\operatorname{O}(n\log n)

Code\mathtt{Code}

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-04-22 23:37:20 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-04-23 00:22:39
 */
#include <iostream>
#include <cstdio>

#define int long long // 好习惯,此处 % 葵 %%%%%%%
//但是这样我真的很烦你知道吗?int所有高亮都变成了紫色,跟 #include 一样
//无语
inline int read() {
    int x = 0;
    bool flag = true;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            flag = false;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    if (flag)
        return x;
    return ~(x - 1);
}

const int mod = 1e8 + 7;

inline int pow(int a, int b = mod - 2) {
    int s = 1;
    for (; b; b >>= 1, (a *= a) %= mod)
        if (b & 1)
            (s *= a) %= mod;
    return s;
}

signed main() {
    int n = read(), m = read();
    int S = pow(2, n) - 1;
    int C = S * (S - 1) % mod * pow(2) % mod;
    if (m <= 2) {
        puts("0");
        return 0;
    }
    int f = 0, g = 0, h = 0;
    for (int i = 3; i <= m; ++i) {
        f = (C - g - h * (S - i + 2 + mod) % mod + mod) % mod * pow(i) % mod;
        (C *= (S - i + 1 + mod) * pow(i) % mod) %= mod;
        h = g;
        g = f;
    }
    printf("%lld\n", f);
    return 0;
}

不错的题2!

posted @   dbxxx  阅读(35)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示