抛硬币
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
【样例解释】
对于第一组数据,当小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)
壮哉我大数学……
像咱这种蒟蒻只能乖乖敲暴力……
(人家大佬都被坑了你一个蒟蒻还在这跳什么)
思路:
想必大家都想到了暴力的方法吧……
正解是一种神奇的东西:
先从特殊情况入手:a=b
假设他们的各种情况构成了一个序列 a+b。
这里咱发现对于每一种小B赢了的情况,都对应的有一个小A赢了的情况,即整个序列的每一位异或上1,就变成小A赢了……
但这不可避免的,每种状态被计算了两次。
于是得到公式:
ans(a,a)=22a−(2aa)2
这就是说,对于所有可能的序列,减去小A正面朝上次数等于小B正面朝上次数的情况,再除以二排除重复。
为什么要减呢?因为相等时翻转了小A还是会输……
对于其他情况,咱发现公式差不多:
ans(a,b)=2a+b+F2
其中F代表翻转以后还是小A赢的情况。
为什么用加F而不用之前的减呢?
因为保证a>=b,在翻转后有些情况小A可以靠总量碾压……
那么我们来推一推F怎么算:
F=∑bi=0(bi)∑a−b−1j=1(ai+j)=∑bi=0∑a−b−1j=1(bb−i)(ai+j)
考虑到在a+b中选b+j的一个方案对应着一组(b-i,i+j)的拆分,得到
F=∑a−b−1i=1(a+bb+i)
于是公式都推完了~
然而,离结束还早呢……
组合数方面,我们可以用扩展Lucas定理去求。
然后就会T……
观察,发现模数质因子仅有2和5,于是简化中国剩余定理部分至无循环。
预处理对于最大位的模数,不整除2和5的所有数的乘积。
即扩展Lucas中的当前层递归要处理的部分。
又发现对于2的快速幂处理部分,无论如何每一层递归都只有1的贡献。
如果是5的快速幂,若为奇数次幂还要多算一个当前位数模数的贡献。
于是递归简化为两个while循环,每一次循环必乘一次当前模拟的递归层的贡献,5的情况特判。
最后,卡常。
——就可以AC了!
真不容易啊( ̄▽ ̄)/
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
ll md,ans,a,b,k;
inline ll qpow(ll a,ll b,ll p)
{
ll ret=1;
while(b)
{
if(b&1ll)
ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return ret;
}
const ll max2=1024,max5=1953125,max10=2000000000,base2=212890625,base5=1787109376;
ll f[max2+3],ff[max5+3];
inline void init()
{
f[0]=1;ff[0]=1;
for(ll i=1;i<=max2;i++)
{
f[i]=f[i-1];
if(i&1)
f[i]=f[i]*i%max2;
}
for(ll i=1;i<=max5;i++)
{
ff[i]=ff[i-1];
if(i%5)
ff[i]=ff[i]*i%max5;
}
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
ll t=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return t;
}
inline ll inv(ll a,ll p)
{
ll x,y;
exgcd(a,p,x,y);
return (x+p)%p;
}
ll calc(ll n,ll a,ll p)//n len
{
if(!n)
return 1;
ll ret=1,t=n/p;
if(a==2)
ret=1;
else
ret=t&1?ff[p]:1;
for(ll i=t*p+1;i<=n;i++)
if(i%a)
ret=ret*i%p;
return ret*calc(n/a,a,p)%p;
}
inline ll calc2(ll n)
{
ll ret=1;
while(n)
{
ret=ret*f[n%max2]%max2;
n>>=1;
}
return ret;
}
inline ll calc5(ll n)
{
ll ret=1;
while(n)
{
if((n/max5)&1)
ret=ret*ff[max5-1]%max5;
ret=ret*ff[n%max5]%max5;
n/=5;
}
return ret;
}
inline ll lucas2(ll n,ll m,ll p)
{
ll cnt=0;
for(ll i=n;i;i>>=1)
cnt+=i/2;
for(ll i=m;i;i>>=1)
cnt-=i/2;
for(ll i=n-m;i;i>>=1)
cnt-=i/2;
return qpow(2,cnt,p)
*calc2(n)%p
*inv(calc2(m),p)%p
*inv(calc2(n-m),p)%p;
}
inline ll lucas5(ll n,ll m,ll p)
{
ll cnt=0;
for(ll i=n;i;i/=5)
cnt+=i/5;
for(ll i=m;i;i/=5)
cnt-=i/5;
for(ll i=n-m;i;i/=5)
cnt-=i/5;
return qpow(5,cnt,p)
*calc5(n)%p
*inv(calc5(m),p)%p
*inv(calc5(n-m),p)%p;
}
inline ll crt(ll a,ll b)
{
return (base5*lucas5(a,b,max5)%max10+base2*lucas2(a,b,max2)%max10)%max10;
}
int main()
{
init();
while(~scanf("%lld%lld%lld",&a,&b,&k))
{
md=pow(10,k)*2;
if(a==b)
ans=(qpow(2,a<<1,md)-crt(a<<1,a)+md)%md;
else
{
ans=qpow(2,a+b,md);
for(ll i=1;i<a-b;i++)
ans=(ans+crt(a+b,b+i))%md;
}
ans/=2;
while(ans<md/20)
putchar('0'),md/=10;
if(ans>0)
printf("%lld\n",ans);
}
return 0;
}