【HNOI2011】卡农

题面

题解

将无序化为有序,最后答案除以$m!$。

设$f[i]$表示选出了$i$个子集,并且满足所有的限制的方案数。

因为转移困难,所以考虑容斥

  1. 限制了每个数的出现次数为偶数,所以如果前$i - 1$个子集是确定的,第$i$个的选择唯一,

    一定是前面选了奇数次的元素的集合。

    所以如果没有其他限制的情况下,选出$i$个自己的方案数为$A_{2^n-1}^{i-1}$

  2. 然后减去第$i$个集合为空的情况,方案数为$f[i-1]$

  3. 然后减去第$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;
}
posted @ 2019-01-04 17:06  xgzc  阅读(155)  评论(1编辑  收藏  举报