[HNOI2011] 卡农

\(\text{Problem}:\)[HNOI2011] 卡农

\(\text{Solution}:\)

对于排列组合问题,将无序转化为有序常常更易求解。对于本题,我们求出有序的答案,除以 \(m!\) 即可得到无序的答案。

\(f_{i}\) 表示选了前 \(i\) 个片段的方案数。由于每个音阶被奏响的次数均为偶数且在片段中至多只会被选择一次,故选取 \(i-1\) 个片段后,第 \(i\) 个片段是被确定的。那么不考虑其他限制时,其方案数为 \(A_{2^{n}-1}^{i-1}\)

对于片段所包含的音阶集合不为空的限制,易知此时前 \(i-1\) 个集合是合法的,故要减去 \(f_{i-1}\)

对于任意两个片段不相同的限制,考虑第 \(i\) 个片段可能与 \(i-1\) 个片段相同。此时我们钦定只有两个片段相同(易知更多片段相同不会被转移来),通过 \(f_{i-2}\) 转移。那么不合法的方案数为 \((i-1)(2^{n}-1-(i-2))f_{i-2}\)

合并以上所有情况,得到 \(f\) 的转移式:

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

总时间复杂度 \(O(n)\)

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=1000010, Mod=1e8+7;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,m,f[N],w;
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
signed main()
{
	n=read(), m=read();
	w=ksc(2,n);
	f[0]=1;
	int fac=1;
	for(ri int i=1,now=1;i<=m;i++)
	{
		f[i]=now;
		f[i]=(f[i]-f[i-1]+Mod)%Mod;
		if(i>=2) f[i]=(f[i]-1ll*(i-1)*(w-i+1+Mod)%Mod*f[i-2]%Mod+Mod)%Mod;
		now=1ll*now*(w-i+Mod)%Mod;
		fac=1ll*fac*i%Mod;
	}
	printf("%d\n",1ll*f[m]*ksc(fac,Mod-2)%Mod);
	return 0;
}
posted @ 2021-05-07 22:15  zkdxl  阅读(44)  评论(0编辑  收藏  举报