[HNOI2011]卡农

来源:互联网 发布:mysql exists if 编辑:程序博客网 时间:2024/04/27 18:49

这道题是day2压轴……去年我没做出来,然后一直以为很难……

昨天做了一下发现这题超过瘾的……是个数学题……写出来的代码超短……

但是很难想……

首先考虑所有的集合:除掉空集以后共有2^n-1个

当确定了前m-1个集合以后,第m个集合就确定了,因为要求所有的数出现偶数次

这道题要求不记顺序,但是记顺序的更好算一点,然后在最后除一个m!即可

那么我们记f[i]为前i个集合记顺序的方案数,g[i]为A(2^n-1,i),即在2^n-1个集合里取i个集合的排列数

很显然f[i]等于g[i]减去某个数,因为算重了

考虑哪些是算重的:

1.前i-1个集合已经保证了所有的数出现偶数次,那么第i个集合就是空集,要减去这种情况,即减去f[i-1]

2.第i个集合和前i-1个集合中的某一个重复,除掉这个重复的集合,有f[i-2]种方案,这个重复的集合可能的位置有i-1种,重复的集合可能的取法有2^n-1-(i-2)种(跟剩下的m-2种不同)

那么f[i]=g[i-1]-f[i-1]-f[i-2]*(i-1)*(2^n-1-(i-2))

最后除个m!,模的数是质数,可以求逆元……然后完了

如果计数的部分解决了就很简单……关键是想不想得到啊……

//Lib#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<ctime>#include<iostream>#include<algorithm>#include<vector>#include<string>#include<queue>#include<stack>#include<set>#include<map>using namespace std;//Macro#definerep(i,a,b)for(int i=a,tt=b;i<=tt;++i)#definedrep(i,a,b)for(int i=a,tt=b;i>=tt;--i)#defineerep(i,e,x)for(int i=x;i;i=e[i].next)#defineirep(i,x)for(__typeof(x.begin()) i=x.begin();i!=x.end();i++)#defineread()(strtol(ipos,&ipos,10))#definesqr(x)((x)*(x))#definepbpush_back#definePSsystem("pause");typedeflong longll;typedefpair<int,int>pii;const int oo=~0U>>1;const double inf=1e100;const double eps=1e-6;string name="canon", in=".in", out=".out";//Varll f[1000008],mi,pre[1000008],mod=100000007,n,m,tmp=1;ll power(ll a,int b){if(b==0)return 1;ll ret=power(a,b>>1);ret=ret*ret%mod;if(b&1)ret=ret*a%mod;return ret;}void Dec(ll &a,ll b){a-=b;if(a<0)a+=mod;}void Init(){scanf("%d%d",&n,&m);mi=power(2,n);mi--;//solve A(2^n-1,i)pre[0]=1;rep(i,1,m)pre[i]=pre[i-1]*(mi-i+1)%mod;} void Work(){f[1]=0;f[2]=0;rep(i,3,m){f[i]=pre[i-1];Dec(f[i],f[i-1]);Dec(f[i],f[i-2]*(i-1)%mod*(mi-(i-2))%mod);}rep(i,2,m)tmp=tmp*i%mod;cout<<f[m]*power(tmp,mod-2)%mod<<endl;}int main(){Init();Work();//PS;return 0;}


原创粉丝点击