【扩展lucas】LOJ#2023. 「AHOI / HNOI2017」抛硬币

Description

  • 抛硬币,小A投 a a a次,小B投 b b b次,求小A正面次数多于小B正面次数的方案数。
  • 1 ≤ a , b ≤ 1 e 15 , 0 ≤ a − b ≤ 1 e 4 , 1 ≤ k ≤ 9 1\le a,b\le 1e15,0 \le a-b\le1e4,1\le k \le 9 1a,b1e15,0ab1e4,1k9

Solution

  • 首先我们先考虑 a = b a=b a=b的情况,不难发现对于一种小A赢的方案数,取反就对应小A输的方案数,所以只要在所有方案中减去平的再除以二即可。
  • 考虑扩展到 a > b a>b a>b的情况,那么小A输或平的情况取反之后对应小A赢,还有一些情况小A取不取反都赢,算出后者,即可算出答案。即 W a > W b , A − W a > B − W b W_a>W_b,A-W_a>B-W_b Wa>Wb,AWa>BWb,则 A − B > W a − W b A-B>W_a-W_b AB>WaWb
  • 这个部分的方案数即为 ∑ i = 1 A − B − 1 ∑ j = 1 b C a i + j C b j = ∑ i = 1 A − B − 1 ∑ j = 1 b C a i + j C b b − j = ∑ i = 1 A − B − 1 C a + b i + b \sum_{i=1}^{A-B-1}\sum_{j=1}^bC_{a}^{i+j}C_b^j=\sum_{i=1}^{A-B-1}\sum_{j=1}^bC_{a}^{i+j}C_b^{b-j}=\sum_{i=1}^{A-B-1}C_{a+b}^{i+b} i=1AB1j=1bCai+jCbj=i=1AB1j=1bCai+jCbbj=i=1AB1Ca+bi+b
  • 然后套用扩展lucas即可。
  • 注意除以二在模1e9的情况下不好计算,可以考虑 ∑ i = 1 A − B − 1 C a + b i + b \sum_{i=1}^{A-B-1}C_{a+b}^{i+b} i=1AB1Ca+bi+b利用组合数的对称性算一半, C 2 a a = 2 C 2 a − 1 a C_{2a}^a=2C_{2a-1}^a C2aa=2C2a1a即可。
#include
#include
#include
#include
#define maxn 2000005
#define mo 1000000000
#define ll long long 
using namespace std;

ll a,b; int ansk;

ll ksm(ll x,ll y,ll p){
	ll s=1;
	for(;y;y/=2,x=x*x%p) if (y&1)
		s=s*x%p;
	return s;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
	if (a%b==0) {x=0,y=1;return b;}
	ll d=exgcd(b,a%b,x,y);
	ll t=x-(a/b)*y; x=y,y=t;
	return d;
}

struct factor{
	int p,pk,c;
	ll s[maxn];
	void prepare(int _p,int _pk,int _c){
		p=_p,pk=_pk,c=_c;
		s[0]=1;for(int i=1;i<pk;i++)s[i]=s[i-1]*((i%p)?i:1)%pk;
	}
	ll inv(ll a){
		ll x,y,d; d=exgcd(a,pk,x,y);
		ll k=pk/d;
		x=x%k+(x<0)*k;
		return x;
	}
	
	ll fct(ll n){
		if (!n) return 1;
		return ksm(s[pk-1],n/pk,pk)*s[n%pk]%mo*fct(n/p)%mo;
	}
	
	ll C(ll n,ll m){	
		ll cnt=0;
		for(ll x=n;x;x/=p) cnt+=x/p;
		for(ll x=m;x;x/=p) cnt-=x/p;
		for(ll x=n-m;x;x/=p) cnt-=x/p;
		if (cnt>=c) return 0;
		return fct(n)*inv(fct(m))%pk*inv(fct(n-m))%pk*ksm(p,cnt,pk)%pk;
	}
} f1,f2;

ll C(ll n,ll m){
	if (n<m) return 0;
	ll a1=f1.C(n,m),a2=f2.C(n,m),p=f1.pk*f2.pk;
	ll c1=a1*f1.inv(f2.pk)*f2.pk%p;
	ll c2=a2*f2.inv(f1.pk)*f1.pk%p;
	return (c1+c2)%p;
}

void out(ll x){
	static int d[30];
	for(int i=1;i<=ansk;i++) d[i]=x%10,x/=10;
	for(int i=ansk;i>=1;i--) putchar('0'+d[i]);
	putchar('\n');
}

int main(){
	freopen("ceshi.in","r",stdin);
	f1.prepare(2,512,9);
	f2.prepare(5,1953125,9);
	while (~scanf("%lld%lld%d",&a,&b,&ansk)){
		ll ans=0;
		if (a==b){
			ans+=ksm(2,a+b-1,mo);
			ans-=C(2*a-1,a);
		} else {
			ans+=ksm(2,a+b-1,mo);
			for(int i=1;a-i>b+i;i++)
				ans+=C(a+b,a-i);
			if ((a+b)%2==0)
				ans+=C(a+b-1,(a+b)/2);
		}
		out((ans%mo+mo)%mo);
	}
}

你可能感兴趣的:(题解,数论)