【洛谷P3214】卡农

题目

题目链接:https://www.luogu.com.cn/problem/P3214
众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。
他将声音分成 \(n\) 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 到 \(n\) 个音阶构成的和声,即从 \(n\) 个音阶中挑选若干个音阶同时演奏出来。
为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。
现在的问题是:小余想知道包含 \(m\) 个片段的音乐一共有多少种。
两段音乐 \(a\)\(b\) 同种当且仅当将 \(a\) 的片段重新排列后可以得到 \(b\)。例如:假设 \(a\)\(\{\{1,2\},\{2,3\}\}\)\(b\)\(\{\{2,3\},\{1,2\}\}\),那么 \(a\)\(b\) 就是同种音乐。
答案对 \(10^8+7\) 取模。
\(n,m\leq 10^6\)

思路

理一下题目内容发现我们需要求出满足以下几个条件的序列数量:

  • 序列中每一个元素所表示的集合元素均在 \([1,n]\) 中且集合互不相同。
  • 每一个数字在所有元素中出现次数之和为偶数。

不难发现我们不需要考虑两个序列“本质相同”,因为最后直接除以 \(m!\) 即可。
考虑 dp。设 \(f_i\) 表示序列前 \(i\) 个元素有多少种方案是合法的。那么考虑容斥:

  1. 无视所有条件,当我们确定了前 \(i-1\) 个元素(保证两两不同)时,我们一定可以唯一确定第 \(i\) 个元素,因为我们转化成了有标号的,所以方案数就是 \(\binom{2^n-1}{i-1}i!\)
  2. 考虑减去重复的情况,假设第 \(i\) 个集合与第 \(j(j<i)\) 个集合完全一致,那么同时删去这两个元素能得到一个长度为 \(i-2\) 的合法序列。除去这 \(i-2\) 个元素以外,\(i,j\)\(2^n-1-(i-2)\) 种可能,\(j\) 可以被放在序列 \(i-1\) 个空隙之间,所以重复的数量是 \((i-1)(2^n-1-(i+1))f_{i-2}\)
  3. 还要减去前 \(i-1\) 个元素已经保证了每一个数字各出现偶数次,这样第 \(i\) 个元素唯一确定是空集,不合法。方案数显然是 \(f_{i-1}\)

所以我们得到

\[f_i=\binom{2^n-1}{i-1}i!-(i-1)(2^n-1-(i+1))f_{i-2}-f_{i-1} \]

组合数递推,其他预处理即可。时间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1000010,MOD=1e8+7;
int n,m;
ll power,f[N],g[N],fac[N],inv[N];

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	fac[0]=inv[0]=inv[1]=g[0]=f[0]=1;
	power=fpow(2,n);
	for (int i=1;i<=m;i++)
	{
		fac[i]=fac[i-1]*i%MOD;
		if (i>1) inv[i]=-1LL*MOD/i*inv[MOD%i]%MOD;
		g[i]=g[i-1]*(power-i)%MOD*inv[i]%MOD;
		f[i]=(g[i-1]*fac[i-1]-f[i-1])%MOD;
		if (i>1) f[i]=(f[i]-f[i-2]*(power-1-(i-2))%MOD*(i-1))%MOD;
	}
	printf("%lld",(f[m]*fpow(fac[m],MOD-2)%MOD+MOD)%MOD);
	return 0;
}
posted @ 2021-01-26 14:48  stoorz  阅读(88)  评论(0编辑  收藏  举报