[bzoj4830][lucas定理][数论]抛硬币

本文探讨了一种基于抛硬币次数的策略游戏,通过数学分析和编程实现,计算了在特定条件下玩家A战胜玩家B的概率,尤其是在玩家A通过作弊手段增加胜利机会的情景下。文章详细介绍了使用组合数学原理解决这一问题的方法。

Description

小A和小B是一对好朋友,他们经常一起愉快的玩耍。最近小B沉迷于**师手游,天天刷本,根本无心搞学习。但是
已经入坑了几个月,却一次都没有抽到SSR,让他非常怀疑人生。勤勉的小A为了劝说小B早日脱坑,认真学习,决
定以抛硬币的形式让小B明白他是一个彻彻底底的非洲人,从而对这个游戏绝望。两个人同时抛b次硬币,如果小A
的正面朝上的次数大于小B正面朝上的次数,则小A获胜。但事实上,小A也曾经沉迷过拉拉游戏,而且他一次UR也
没有抽到过,所以他对于自己的运气也没有太大把握。所以他决定在小B没注意的时候作弊,悄悄地多抛几次硬币
,当然,为了不让小B怀疑,他不会抛太多次。现在小A想问你,在多少种可能的情况下,他能够胜过小B呢?由于
答案可能太大,所以你只需要输出答案在十进制表示下的最后k位即可。

Input

有多组数据,对于每组数据输入三个数a,b,k,分别代表小A抛硬币的次数,小B抛硬币的次 数,以及最终答案保留多少位整数。
1≤a,b≤10^15,b≤a≤b+10000,1≤k≤9,数据组数小于等于10。

Output

对于每组数据,输出一个数,表示最终答案的最后k位为多少,若不足k位以0补全。

Sample Input

2 1 9

3 2 1

Sample Output

000000004

6

HINT

【样例解释】

对于第一组数据,当小A抛2次硬币,小B抛1次硬币时,共有4种方案使得小A正面朝上的

次数比小B多。(01,0),(10,0),(11,0),(11,1)

对于第二组数据,当小A抛3次硬币,小B抛2次硬币时,共有16种方案使得小A正面朝上的次数比小B多。(001,00),

(010,00),(100,00),(011,00),(101,00),(110,00),(111,00),(011,01),(101,01),(110,01),(111,01),(011,10),

(101,10),(110,10),(111,10),(111,11)

题解

思路还是非常神仙的…
不妨把抛硬币的操作看成01序列
先从A=B开始考虑
01接起来,显然是个2*A的序列
这时候每个A赢的方案都对应了一个01反转之后B赢的方案
我们需要去掉的就只有A和B平手的方案
就是∑CAi∗CBi\sum C_{A}^{i}*C_{B}^{i}CAiCBi,换一下就是∑CAiCBB−i\sum C_{A}^{i}C_{B}^{B-i}CAiCBBi
就是2*A的前A个选i个,后A个选A-i个,那么就是
2A+B−C2AA2\frac{2^{A+B}-C_{2A}^{A}}{2}22A+BC2AA
拓展到A≠BA\neq BA̸=B
在某些A赢的情况下01反转后仍然还可以做到是B赢
然而B赢的情况01反转后一定是A赢的方案
那其实去掉的是A赢反转后仍然是A赢的方案
枚举一个B是1的数量i,再枚举A比他多的j
于是就要满足不等式A−(i+j)>B−iA-(i+j)>B-iA(i+j)>Bi
移项可知j&lt;=A−B−1j&lt;=A-B-1j<=AB1
不合法的方案数就是∑i=0BCBi∗∑j=1a−b−1CAi+j\sum_{i=0}^{B}C_{B}^i*\sum_{j=1}^{a-b-1}C_{A}^{i+j}i=0BCBij=1ab1CAi+j
变一下就是∑i=0BCBB−i∗∑j=1a−b−1CAi+j\sum_{i=0}^{B}C_{B}^{B-i}*\sum_{j=1}^{a-b-1}C_{A}^{i+j}i=0BCBBij=1ab1CAi+j
这个柿子实际上等价于前B个选择了B-i个1,后A个选择了i+j个1
那么就是∑j=1A+B−1CA+BB+j\sum_{j=1}^{A+B-1} C_{A+B}^{B+j}j=1A+B1CA+BB+j,可以知道这种状态下的答案是
2A+B+∑j=1A+B−1CA+BB+j2\frac{2^{A+B}+\sum_{j=1}^{A+B-1}C_{A+B}^{B+j}}{2}22A+B+j=1A+B1CA+BB+j
扩展lucas玩玩
然后就到了卡常阶段
发现模数的质因子固定,可以先把阶乘预处理
然后根据杨辉三角 组合数只用算一半
然后就可以了…
其实卡了一早上

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline LL read()
{
	LL f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}

int f1[3100000],f2[3100000];
void init(int md1,int md2)
{
	f1[0]=f2[0]=1;
	for(int i=1;i<=md1;i++)f1[i]=(LL)f1[i-1]*((i%2)?i:1)%md1;
	for(int i=1;i<=md2;i++)f2[i]=(LL)f2[i-1]*((i%5)?i:1)%md2;
}
int pow_mod(int a,LL b,int mod)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=(LL)ret*a%mod;
		a=(LL)a*a%mod;b>>=1;
	}
	return ret;
}
int exgcd(int a,int b,int &x,int &y)
{
	if(a==0)
	{
		x=0;y=1;
		return b;
	}
	else
	{
		int tx,ty;
		int d=exgcd(b%a,a,tx,ty);
		x=ty-(b/a)*tx;
		y=tx;
		return d;
	}
}
int getinv(int a,int mod)
{
	int AA=a,BB=mod,K=1,x,y;
	int d=exgcd(AA,BB,x,y);
	x=((LL)x*(K/d)%(BB/d)+(BB/d))%(BB/d);
	return x;
}
int solve(LL n,int pi,int mod)
{
	if(!n)return 1;
	int ret=1;
	if(pi==2)ret=(LL)ret*f1[mod]%mod;
	else ret=(LL)ret*f2[mod]%mod;
	ret=pow_mod(ret,n/mod,mod);
	if(pi==2)ret=(LL)ret*f1[n%mod]%mod;
	else ret=(LL)ret*f2[n%mod]%mod;
	return (LL)ret*solve(n/pi,pi,mod)%mod;
}
int lim;
int exlucas(LL n,LL m,int pi,int mod,bool tf)
{
	int ok=0;
	for(LL i=n;i;i/=pi)ok+=i/pi;
	for(LL i=m;i;i/=pi)ok-=i/pi;
	for(LL i=n-m;i;i/=pi)ok-=i/pi;
	if(tf&&pi==2)ok--;
	if(ok>=lim)return 0;
	int a=solve(n,pi,mod),b=solve(m,pi,mod),c=solve(n-m,pi,mod);
	
	return (LL)a*getinv(b,mod)%mod*getinv(c,mod)%mod*pow_mod(pi,ok,mod)%mod;
}
LL p1[5];
int C(LL n,LL m,int mod,bool tf)
{
	if(n<m)return 0;
	p1[1]=p1[2]=1;
	while(!(mod%2)&&mod)p1[1]*=2,mod/=2;
	while(!(mod%5)&&mod)p1[2]*=5,mod/=5;
	int b1=exlucas(n,m,2,p1[1],tf),m1=p1[1];
	int b2=exlucas(n,m,5,p1[2],tf),m2=p1[2];
	if(tf)b2=(LL)b2*getinv(2,p1[2])%p1[2];
	int A=m1,B=m2,K=b2-b1,x,y;
	int d=exgcd(A,B,x,y);
	x=((LL)x*(K/d)%(B/d)+(B/d))%(B/d);
	return ((LL)m1*x+b1)%(p1[1]*p1[2]);

}
LL A,B,K;
void ot(LL u,int k)
{
	int ret=0,g=u;
	while(g)ret++,g/=10;
	for(int i=1;i<=k-ret;i++)putchar('0');
	pr2(u);
}
int main()
{
	init(512,1953125);
	while(scanf("%lld%lld%lld",&A,&B,&K)!=EOF)
	{
		LL o=1,n1=1,n2=1,cnt=0;lim=K;
		while(cnt+1<=K)o*=10,n1*=2,n2*=5,cnt++;
		swap(o,K);
		if(A==B)
		{
			int temp=pow_mod(2,A+B-1,K);
			temp=(temp-C(2*A,A,K,1)+K)%K;
			ot(temp,o);
		}
		else
		{
			int temp=pow_mod(2,A+B-1,K);
			if(!((A+B)&1))
			{
				temp+=C(A+B,(A+B)/2,K,1);
				if(temp>=K)temp-=K;
            }
            for(LL i=(A+B)/2+1;i<=A-1;++i)
            {
            	temp+=C(A+B,i,K,0);
            	if(temp>=K)temp-=K;
            }
            ot(temp,o);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值