[HNOI2011]卡农

这道题是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
#define	rep(i,a,b)	for(int i=a,tt=b;i<=tt;++i)
#define	drep(i,a,b)	for(int i=a,tt=b;i>=tt;--i)
#define	erep(i,e,x)	for(int i=x;i;i=e[i].next)
#define	irep(i,x)	for(__typeof(x.begin()) i=x.begin();i!=x.end();i++)
#define	read()	(strtol(ipos,&ipos,10))
#define	sqr(x)	((x)*(x))
#define	pb	push_back
#define	PS	system("pause");
typedef	long long	ll;
typedef	pair<int,int>	pii;
const int oo=~0U>>1;
const double inf=1e100;
const double eps=1e-6;
string name="canon", in=".in", out=".out";
//Var
ll 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值