! AHOI/HNOI2017抛硬币



显然(ans=sum^a_{i=1}C^i_asum^{min(b,i-1)}_{j=1}C^j_b)但显然超时

SOL:

换一种方式来思考

(a=b)

一种失败方案翻转即成功方案,答案为(总方案-不输不赢)除以2

不输不赢(sum_{i=0}^aC_a^iC_a^i=sum_{i=0}^aC_a^iC_a^{a-i})

相当于从(2a)中选出(a)(C_{2a}^a)

(ans=frac{2^{2a}-C_{2a}^a}2)

(a!=b)

答案为(总方案+翻面还是赢的方案数)除以二

设A正面朝上y次,B正面朝上x次

(y>x,b-x<a-y o a-b>y-x)

翻面还是赢(sum_{i=0}^bC^i_bsum_{j=1}^{a-b-1}C^{i+j}_a)

(sum^{a-b-1}_{j=1}sum_{i=0}^bC^{b-i}_{b}C^{i+j}_a=sum^{a-b-1}_{j=1}C^j_{a+b})

(这个化简可以和刚才差不多,但实际为范德蒙德卷积)

(ans=frac{2^{a+b}+sum^{a-b-1}_{j=1}C^j_{a+b}}2)

模数十的倍数,需要扩展卢卡斯

除二无逆元,只好先除(2^{a+b} o2^{a+b-1})

(sum^{a-b-1}_{j=1}C^j_{a+b})实际对称的只有当(a+b)为偶数时(C^{(a+b)/2}_{a+b})只有一项

(C_{2a}^a=C_{2a-1}^a+C_{2a-1}^{a-1}=2C_{2a-1}^a)解决

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int ksm(int x,int r,int mod){
	int ret=1;
	for(int i=0;(1ll<<i)<=r;i++){
		if((r>>i)&1)ret=ret*x%mod;
		x=x*x%mod;
	}
	return ret;
} 
inline void exgcd(int a,int b,int &x,int &y){
	if(!b){x=1;y=0;return;}
	exgcd(b,a%b,x,y);
	int t=y;y=x-a/b*y;x=t;
}
inline int inv(int a,int mod){
	if(!a)return 0;
	static int x,y;
	exgcd(a,mod,x,y);
	return (x%mod+mod)%mod;
}
int fac[2][2000004];
int jie(int x,int p,int pk){
	if(!x)return 1;
	int ret=fac[p!=2][pk];
	ret=ksm(ret,x/pk,pk)*fac[p!=2][x%pk]%pk;
	return ret*jie(x/p,p,pk)%pk;
}
int a,b,K,mod,ans,k2,k5;
inline int C(int d,int u,int p,int pk){
	if(d<u)return 0;
	int js=0;
	for(int i=d;i;i/=p)js+=i/p;
	for(int i=u;i;i/=p)js-=i/p;
	for(int i=d-u;i;i/=p)js-=i/p;
	if(js>=K)return 0;
	int s1=jie(d,p,pk),s2=jie(u,p,pk),s3=jie(d-u,p,pk);
	int ret=ksm(p,js,pk)*s1%pk*inv(s2,pk)%pk*inv(s3,pk)%pk;
	return ret*(mod/pk)%mod*inv(mod/pk,pk)%mod;//crt
}
inline int lucas(int d,int u){
	return (C(d,u,2,k2)+C(d,u,5,k5))%mod;
}
inline void init(int p,int pk){
	int c=(p!=2);
	fac[c][0]=1;
	for(int i=1;i<=pk;i++)
		if(i%p)fac[c][i]=fac[c][i-1]*i%pk;
		else fac[c][i]=fac[c][i-1];
}
signed main(){
	init(2,512);init(5,1953125);
	while(~scanf("%lld%lld%lld",&a,&b,&K)){
		mod=ksm(10,K,1e9+5);
		k2=ksm(2,K,1e9+5);k5=ksm(5,K,1e9+5);
		ans=ksm(2,a+b-1,mod);
		if(a==b)ans=(ans-lucas((a<<1)-1,a)+mod)%mod;
		else{
			for(int i=(a+b)/2+1;i<a;i++)ans=(ans+lucas(a+b,i))%mod;
			if((a+b)%2==0)ans=(ans+lucas(a+b-1,a+b>>1))%mod;
		} 
		while(ans<mod/10){putchar('0');mod/=10;}
		cout<<ans<<"
";
	}
	return (0-0);
}
原文地址:https://www.cnblogs.com/aurora2004/p/12535086.html