[HNOI2011] 卡农
去除相同情况是繁琐的,再加上这些集合本身是严格互异的,不会出现重复的问题,所以把它当成有序集合序列来做,最后除以阶乘即可。
然后考虑有序的情况下怎么做。用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;
}
一如既往,万事胜意