bzoj4402 claris的剑 数论

       这道题目看起来好华丽啊。。我一开始还以为是递推找规律,然而看到了本质不同以后就。。。。

       实际上,任意一种可行的方案的序列都可以转化为本质相同的下述序列:

       先是若干个(1,2),然后是若干个(3,2),然后是若干个(3,4)……以此类推,当然如果长度为奇数就在最后面加上一个数,举个例子:

       {1,2,3,2,3,2,3,2,3,4,5,4,5,4,5,4,5,6,5,6,5,6,7}

       可以这么证明,首先对于1,1的后面一定跟着一个2,前面也是1个2,那么就把所有的(1,2)都不断移到最前面,显然这个序列仍然符合要求;然后再把(3,2)移到若干个(1,2)的后面,再把(3,4)移到(3,2)的后面……最后就变成上面所描述的序列了。

       这样,如果两个序列,符合上面的描述且至少有一位不同,显然这两个序列是本质不同的。这样就解决了本质不同这个问题;同时,将序列有序化了。

       假设序列长度为n,最大值为m,n>=m,那么此时有多少种本质不同的方案呢?首先得到序列{1,2,...,m},接下来就是考虑往序列里面加东西使它的长度到大n。显然如果加了,就只能一对一对加,即(1,2)(3,2)(3,4)(5,4)……这些2个2个往里面加,这样总共可以加[(n-m)/2]次,如果最后多了一个,那也只有一种情况了:加在最后面。由于加入的先后顺序没有影响,所以相当于[(n-m)/2]个球里放在(m-1)个盒子里,允许空盒的方案数。这个用组合计数解决就行了。

       (补充:m个相同的球放在n个盒子里,允许空盒的方案数为C(m+n-1,n-1),把这个记为F(m,n))

       当n,m一定时,答案为F([(n-m)/2],m-1),但这样是O(MN)的。注意到F(0,m-1)+F(1,m-1)+F(2,m-1)+...+F([(n-m)/2],m-1)=F([(n-m)/2],m),这样就变为O(M+N)的了。

AC代码如下:

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#define ll long long
#define mod 1000000007
using namespace std;

int n,m,f[2000005],inv[2000005];
int solve(int x,int y){
	if (x<0) return 0; x>>=1; y--;
	return (ll)f[x+y]*inv[x]%mod*inv[y]%mod;
}
int main(){
	scanf("%d%d",&n,&m); int i;
	f[0]=f[1]=inv[0]=inv[1]=1;
	for (i=2; i<=n; i++){
		f[i]=(ll)f[i-1]*i%mod; inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
	}
	for (i=2; i<=n; i++) inv[i]=(ll)inv[i]*inv[i-1]%mod;
	int ans=0; if (m&&n) ans=1; m=min(n,m);
	for (i=2; i<=m; i++){
		ans=((ans+solve(n-i,i))%mod+solve(n-i-1,i))%mod;
	}
	printf("%d\n",ans);
	return 0;
}
</span>

by lych

2016.1.30

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值