以前遇到过的模型突然攻击我,被偷袭了。
description
有一个全集 $\{1,2,3,\dots,n\}$,你需要选出 $m$ 个其非空子集,这些子集要互不相同,且 $1\sim n$ 在这些子集里的出现次数都是偶数。计数方案。
$n,m\leq 10^6$。
solution
偶数的这个限制比较诡异,注意到我们几乎可以任意选集合,有一个常见的想法(可我忘了)是随便选前 $m-1$ 个集合,使用最后一个集合调整奇偶性,方案数就是 $\dbinom{2^n-1}{m-1}$。
这样显然会算进去一些不合法的,用容斥减掉,设 $f(i)$ 表示 $m=i$ 的答案。不合法的方案有三类:
-
第 $i$ 个集合是空集,这样就说明前 $i-1$ 个已经是合法的方案了,方案数是 $f(i-1)$。
-
第 $i$ 个集合与之前的集合重复了。这时我们钦定这个重复的集合,剩下的 $i-2$ 个集合一定是合法的,方案数为 $f(i-2)\times (2^n-1-(i-2))$
然后因为每个方案都会被计算 $i$ 次,要把答案除以 $i$。
$$ f(i)=\dfrac{\dbinom{2^n-1}{m-1}-f(i-1)-f(i-2)\times(2^n-i+1)}{i} $$
那个组合数预处理下降幂就可以算了。
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
namespace solve
{
const int maxn = 1e6 + 10;
const int mod = 1e8 + 7;
typedef long long ll;
ll qpow(ll a, ll x, ll p)
{
ll res = 1;
for (; x; x >>= 1, a = a * a % p)
if (x & 1)
res = res * a % p;
return res;
}
ll fac[maxn], ifac[maxn], mi[maxn];
ll down[maxn];
int n;
void init(int n = 1e6)
{
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2, mod);
for (int i = n - 1; i >= 0; i--)
ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
int m;
ll f[maxn];
void main()
{
init();
cin >> n >> m;
ll x = qpow(2, n, mod) - 1;
down[0] = 1;
down[1] = x;
for (int i = 2; i <= m; i++)
down[i] = down[i - 1] * (down[1] - i + 1 + mod) % mod;
f[0] = 1, f[1] = 0;
for (int i = 2; i <= m; i++)
f[i] = qpow(i, mod - 2, mod) * (down[i - 1] * ifac[i - 1] % mod - f[i - 1] - f[i - 2] * (qpow(2, n, mod) - 1 - (i - 2)) % mod) % mod;
cout << (f[m] + mod) % mod << endl;
}
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0);
int T = 1;
// cin >> T;
while (T--)
solve::main();
}