【bzoj2339】【HNOI2011】【卡农】【组合数学+dp】

来源:互联网 发布:淘宝空间怎么批量删除 编辑:程序博客网 时间:2024/04/30 00:03

Description

题解:

我们先把它变成有序的,最后除一个m!
对于有序的方案数我们考虑补集转换。
首先所有的子集个数应该是2^n-1;
我们定义f[i]为使用i个盘子的方案数。
因为要保证总数是偶数。也就是说如果你确定了i-1个盘子第i个盘子也就确定了
所以总数应该是A(2^n-1,i-1);
这样肯定多算了。具体来说有两部分。
1.如果前i-1个已经合法,那么第i个就是空集。这样肯定不合法。所以要减去f[i-1]
2.如果根据前i-1个确定出来的第i个集合和前面的某一个重复,这样肯定是不合法的。
对于第2种情况。
因为考虑顺序,所以那个和第i个重复的集合有i-1种位置.
对于每种位置。当前已经满足偶数了,去掉两个集合之后还是偶数。
所以剩下其他集合的方案数为f[i-2]。
然后我们需要算出有多少种可能重复的方案
因为我们已经确定了(i-2)个位置,所以方案数为(2^n-1-(i-2));
最后再乘上一个m!关于P的逆元即可。
代码:
#include<iostream>#include<cstdio>#define N 1000010#define P 100000007using namespace std;long long n,m,p[N],f[N],temp;long long power(long long a,long long b){    long long ans(1);    for(long long i=b;i;i>>=1,(a*=a)%=P) if(i&1)(ans*=a)%=P;    return ans;}void pre(){  p[0]=1;  for (long long i=1;i<=m;i++) p[i]=(p[i-1]*((temp-i+1+P)%P))%P;}int main(){    scanf("%lld%lld",&n,&m);    temp=power(2,n);temp--;    if (temp<0) temp+=P;    pre();    for (long long i=3;i<=m;i++)       f[i]=((p[i-1]-f[i-1]-f[i-2]*(i-1)%P*(temp-(i-2))%P)+P)%P;    temp=1;    for (long long i=1;i<=m;i++) (temp*=i)%=P;    (f[m]*=power(temp,P-2))%=P;    cout<<f[m];}





0 0