[ARC096C] Everything on It 补题记录

题目链接#

题目大意:
对于集合 {1,2,,n} ,求它的子集族中,有多少个满足:

  • 任意两个子集互不相同;
  • 1,2,,n 都在其中至少出现了 2 次。

答案对 M 取模。

看到这种东西就要想到容斥。

Fi 表示至少有 i 个数字只出现了一次。
更具体的,就是 Fi 个数只出现一次,其他的数出现次数随便。
由容斥我们可以知道:

Ans=i=0n(1)iFi

我们来考虑这时 ni 个随便的部分构成的方案数。
首先,这 ni 个数随便定是否只出现了一次,可以看出方案数是 2ni
然后对于每一个得出的情况,我们都可以选或不选,所以就是 22ni

再来看从 n 个数选出 i 个一定出现一次的方案数 Cni 很简单。
如果我们设 fi 表示恰好有 i 个数只出现了一次,那么可以得到:

Fi=fi×22ni×Cni

现在着手考虑 fi 是怎么得到的。
我们设 gi,j 表示 i 个只出现一次的数放到 j 个集合的方案数。

  • 当此时第 j 个集合没有不合法的数,此时 i 只能填在此处 gi1,j1
  • 意味着 j 个集合里面都有不合法的数字了,这样的话第 i 个不合法的数可以选择加入到 j 个集合中任意一个,也可以不加入任何集合。(因为不是强制的) (j+1)gi1,j

非常显然的 gi,j 自然是这两种情况的和。

gi,j=gi1,j1+(j+1)gi1,j

最后我们枚举有几个集合里有非法的元素就可以了。
注意:除了不合法的数字一个集合中还可以有其他的元素,及 (2ni)j

fi=j=0igi,j×(2ni)jAns=i=0n(1)i×j=0igi,j×(2ni)j×22ni×Cni

呃呃呃,这个 Ans 的式子可能有一点点乱。。。

Code#

#include <cstdio>
#include <iostream>
#include <algorithm>

#define file(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)

#define Enter putchar('\n')
#define quad putchar(' ')

#define int long long 
const int N = 3005;

int n, mod, fac[N], g[N][N], ans;

inline int power(int a, int n, int mod);
inline int C(int n, int m);

signed main(void) {
  std::cin >> n >> mod;
  fac[0] = 1;
  for (int i = 1; i <= n; i++)
    fac[i] = fac[i - 1] * i % mod;
  for (int i = 0; i <= n; i++) {
    g[i][0] = 1;
    for (int j = 1; j <= i; j++)
      g[i][j] = (g[i - 1][j - 1] + (j + 1) * g[i - 1][j] % mod) % mod;
  }
  for (int i = 0, lala; i <= n; i++) {
    int two = power(2, n - i, mod - 1);
    two = power(2, two, mod);
    int num = power(2, n - i, mod), F = 0, mul = 1;
    for (int j = 0; j <= i; j++) {
      F = (F + g[i][j] * mul) % mod;
      mul = mul * num % mod;
    }
    if (i % 2 == 1) lala = mod - C(n, i);
    else lala = C(n, i);
    ans = (ans + F * lala % mod * two % mod) % mod;
    ans = (ans % mod + mod) % mod;
  }
  std::cout << ans << std::endl;
  return 0;
}

inline int power(int a, int n, int mod) {
  int ret = 1;
  while (n) {
    if (n & 1) ret = ret * a % mod;
    a = a * a % mod;
    n /= 2;
  }
  return ret;
}
inline int C(int n, int m) {
  if (n < m) return 0;
  int ret = fac[n];
  ret = ret * power(fac[m], mod - 2, mod) % mod;
  ret = ret * power(fac[n - m], mod - 2, mod) % mod;
  return ret;
}

作者:Aonynation

出处:https://www.cnblogs.com/Oier-GGG/p/16345764.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Aonynation  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示