[HNOI2011] 卡农

link

去除相同情况是繁琐的,再加上这些集合本身是严格互异的,不会出现重复的问题,所以把它当成有序集合序列来做,最后除以阶乘即可。

然后考虑有序的情况下怎么做。用fi来代表选出i段合法音乐的方案数,考虑用整体减空白的方法。由于要求所有音阶都必须出现偶数次,所以假如i-1个集合定了那么剩下的那个集合也就唯一了。于是满足这一条件的方案数为 \(A_{2^m-1}^{i-1}\) ,去除空集合后的所有方案选i-1个不同的即可。然后考虑空集,直接钦定最后一个是空集,那么发现余下的集合也能满足音阶出现偶数次的限制条件,所以方案数是 fi-1 。再然后就是要保证这个被确定的集合不能和前面的相同,那就钦定有两个集合相同好了。先确定那个元素如此不幸,有i-1种情况;然后看这两个元素是啥,有 \(2^m-1-(i-2)=2^m-i+1\) 种情况;剩下的元素自然是 fi-2 ,毕竟两个相同的集合并不会妨碍元素出现偶数次。然后递推即可。

排列那里需要预处理, \(2^m\) 也需要预处理。排列那里直接用最原始的式子就可以了,i的范围小。

还有就是不能让三个数直接相乘,不论如何都要取模。

#include<bits/stdc++.h>
//#define zczc
#define int long long
const int N=1000010;
const int mod=1e8+7;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,n,f[N],A[N];
inline int qpow(int s1,int s2){
	if(s2==1)return s1;
	int an=qpow(s1,s2>>1);
	if(s2&1)return an*an%mod*s1%mod;
	else return an*an%mod;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	int e2=1;
	for(int i=1;i<=m;i++)e2*=2,e2%=mod;
	A[0]=1;
	for(int i=1;i<=n;i++)A[i]=A[i-1]*(e2-i+mod)%mod;
	f[0]=1,f[1]=0;
	for(int i=2;i<=n;i++){
		f[i]=A[i-1];
		f[i]-=f[i-1];
		f[i]-=f[i-2]*(i-1)%mod*(e2-i+1);
		f[i]=(f[i]%mod+mod)%mod;
	}
	int sr=1;
	for(int i=1;i<=n;i++)sr*=i,sr%=mod;
	//printf("%lld\n",f[n]);
	printf("%lld",f[n]*qpow(sr,mod-2)%mod);
	
	return 0;
}
posted @ 2022-06-27 21:35  Feyn618  阅读(15)  评论(0编辑  收藏  举报