【HNOI2011】卡农
题面
题解
将无序化为有序,最后答案除以$m!$。
设$f[i]$表示选出了$i$个子集,并且满足所有的限制的方案数。
因为转移困难,所以考虑容斥
-
限制了每个数的出现次数为偶数,所以如果前$i - 1$个子集是确定的,第$i$个的选择唯一,
一定是前面选了奇数次的元素的集合。
所以如果没有其他限制的情况下,选出$i$个自己的方案数为$A_{2^n-1}^{i-1}$
-
然后减去第$i$个集合为空的情况,方案数为$f[i-1]$
-
然后减去第$i$个集合与之前某个子集相同的情况。
如果将这两个相同的集合删去,剩下的集合一定合法,方案数为$f[i-2]$。
又第$i$个子集有$2^n-1-(i-2)$种方案,同时和第$i$个子集相同的集合的位置有$i-1$种,
所以方案数为$f[i-2]\times(i-1)\times(2^n-1-(i-2))$
所以转移为
$$ f[i]=A_{2^n-1}^{i-1}-f[i-1]-(f[i-2]\times(i-1)\times(2^n-1-(i-2))) $$
边界$f[0]=1,f[1]=0$
真毒瘤
代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x))
const int maxn(1000010), Mod(100000007);
int n, m, f[maxn], Inv, A[maxn], Pow;
int fastpow(int x, int y)
{
int ans = 1;
while(y)
{
if(y & 1) ans = 1ll * ans * x % Mod;
x = 1ll * x * x % Mod, y >>= 1;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
f[0] = A[0] = Inv = 1;
for(RG int i = 2; i <= m; i++) Inv = 1ll * Inv * i % Mod;
Inv = fastpow(Inv, Mod - 2); Pow = (fastpow(2, n) - 1 + Mod) % Mod;
for(RG int i = 1; i <= m; i++) A[i] = 1ll * A[i - 1] * (Pow - i + 1) % Mod;
for(RG int i = 2; i <= m; i++)
{
f[i] = (A[i - 1] - f[i - 1] + Mod) % Mod;
f[i] = (f[i] - 1ll * f[i - 2] * (i - 1)
% Mod * (Pow - (i - 2)) % Mod) % Mod;
f[i] = (f[i] + Mod) % Mod;
}
printf("%lld\n", 1ll * f[m] * Inv % Mod);
return 0;
}