[Codeforces 336D] Vasily the Bear and Beautiful Strings //组合数学

题目链接

题意:定义modification操作为取string(长度>=2)的最后两个字符,如果为(01,10,11)则将这两个字符变为0;如果为00,则将其变为1。
给定n,m,g [0<=n,m<=1e5,0<=g<=1]
问有多少个不同的string,符合以下条件:
①string中有n个0,m个1
②string经过任意次modification操作后,string变为字符g

思路:首先要知道 n个0 和 m个1 的排列为 ( n + m ) ! n ! ∗ m ! \frac{(n+m)!}{n!*m!} n!m!(n+m)!
然后,从一个字符开始:设当前剩余k0个0,k1个1
(1) 如果g是0,可以把它变成11、10、01
····· 对于11和10,不看第一个1,第二个数是0或1都可以,所以直接求 k0个0 和 (k1-1)个1 的排列累加到ans即可。(程序里是先判断了k1是否不为0)
····· 对于01,就是用掉一个0,然后把g变成1,继续走
(2) 如果g是1,只能把它变成00,即用掉一个0,然后把g变成0,继续走
因为两个操作都是用掉一个0,所以循环n次就用完了所有的0。
最终,0全用完,剩余一个下一位的g 和 k1个1,但是有如下情况也是合法却没被排列公式计算到的:
… ①如果g是1,因为1只能变成00,但是已经没有剩余的0了,所以此时允许k1是0或1,(0是因为直接舍弃这个g,1是因为让这一位就是1)
… ②如果g是0,因为0可以变成11、10、01,这里,如果k1为2,我们可以直接把g变成11;如果k1为3,我们可以先把g变成10,然后把后面的0变成11,即111;如果k1为4,同理,先变出来110,再把后面的0变成11,就有了1111,etc. 即此时k1可以为>=2的任意值
所以如果是上述(1)(2)两个情况的话,结果应该+1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll p=1e9+7;
const int N=2e5+7;
ll n,m,jc[N],ans;
int g;
void init(){//预处理阶乘
	jc[0]=1;
	for(ll i=1;i<=2e5;i++)jc[i]=jc[i-1]*i%p;
}
ll qpow(ll x,ll e,ll mod){
	ll re=1;
	while(e){
		if(e&1){
			re=re*x%mod;
		}
		x=x*x%mod;
		e>>=1; 
	} 
	return re%mod;
}
ll A(ll a,ll b){//a个0 和 b个1的排列
	ll up=jc[a+b],down=jc[a]*jc[b]%p;
	ll re=up*qpow(down,p-2,p)%p;
	return re;
}
int main(){
	init();
	cin>>n>>m>>g;
	ll k0=n,k1=m;
	for(ll i=1;i<=n;i++){
		if(g==0){
			g=1;
			if(k1)ans=(ans+A(k0,k1-1))%p;
			k0--;
		}else{
			g=0;
			k0--;
		}
	}
	if((g==1&&k1<2)||(g==0&&k1>=2))ans=(ans+1)%p;
	cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkscx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值