title
简化题意:
两人抛硬币,小 \(A\) 抛硬币的次数为 \(a\) ,小 \(B\) 抛硬币的次数为 \(b\) ,在多少种可能的情况下,他能够胜过小 \(B\) 呢?只需要输出答案在十进制表示下的最后 \(k\) 位即可。
analysis
首先,我们考虑一下 \(a=b\) 的情况。
那么对于一种 \(B\) 获胜的方案序列(比如二人都抛 \(3\) 次,\(A\) 第三次正面朝上,\(B\) 第一次和第二次正面朝上,序列就是 001110
),将每一位都异或 \(1\) ,就变成了 \(A\) 获胜的。
当然,还要减去平局(每一种平局将后半部分反序,一共有 \(a\) 个 \(1\) ,所以平局数是 \(C_{a+b}^a\) )。
所以最终答案就是:
\[ \frac{2^{a+b}-C^a_{a+b}}{2}=\frac{2^{2a}-C^a_{2a}}{2} \]
然后考虑 \(a>b\) 的情况( \(a<b\) 的情况,\(A\) 怎么获胜?就不讨论了)。
同样的,将一种 \(B\) 获胜的方案序列的每一位都异或 \(1\) ,就变成了 \(A\) 获胜的。但是现在 \(A\) 抛硬币的次数多啊,可以通过数量取胜,所以就会出现把一种 \(A\) 获胜的方案序列的每一位都异或 \(1\) 后还是 \(A\) 获胜的序列的情况。
这样的序列假设 \(B\) 正面朝上 \(i\) 次, \(A\) 比 \(B\) 多正面朝上 \(j\) 次,就应该满足 \(b−i<a−i−j\) ,种类数就是:
\[ \sum^b_{i=0}\sum^{a-b-1}_{j=1}C^i_bC^{i+j}_{a}=\sum^b_{i=0}\sum^{a-b-1}_{j=1}C^{b-i}_bC^{i+j}_{a} \]
那么我们这么看这个式子:我们从 \(a+b\) 个数组成的序列中选取 \(b+j\) 个元素变成 \(1\) ,然后由于在前 \(b\) 个元素中 \(1\) 的个数就正好可以能完成那个和 \(i\) 有关的枚举,所以上式就等同于:
\[ \sum^{a-b-1}_{i=1}C^{b+i}_{a+b} \]
那么最终答案就是(平局的方案序列取反就能变成获胜序列了,因为 \(A\) 数量多嘛):
\[ \frac{2^{a+b}+\sum^{a-b-1}_{i=1}C^{b+i}_{a+b}}{2} \]
那么组合数的计算就用 \(exLucas\) 吧(模数不一定为质数),而且由于在杨辉三角中组合数具有对称性,所以只需要计算一半组合数就好了,然后卡卡常就好了。
code
#include<bits/stdc++.h>
typedef long long ll;
const int maxn=2e6+10;
namespace IO
{
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::write;
inline ll Quick_power(ll a,ll b,ll mod)
{
ll ans=1;
while (b)
{
if (b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
if (!b)
{
x=1, y=0;
return ;
}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
}
inline ll inv(ll n,ll m)
{
ll x,y;
exgcd(n,m,x,y);
return x=(x%m+m)%m;
}
ll fac[2][maxn];
inline ll mul(ll n,int p,ll pk)
{
if (!n) return 1;
ll res=fac[p!=2][pk];
res=Quick_power(res,n/pk,pk)%pk*fac[p!=2][n%pk]%pk;
return res*mul(n/p,p,pk)%pk;
}
ll k,mod,ans,k2,k5;
inline ll C(ll n,ll m,int p,ll pk,bool div)
{
if (n<0 || m<0 || n<m) return 0;
ll zero=0;
for (ll i=n; i; i/=p) zero+=i/p;
for (ll i=m; i; i/=p) zero-=i/p;
for (ll i=n-m; i; i/=p) zero-=i/p;
if (p==2 && div) --zero;
if (zero>=k) return 0;//优化
ll a=mul(n,p,pk), b=mul(m,p,pk), c=mul(n-m,p,pk);
ll res=Quick_power(p,zero,pk)*a%pk*inv(b,pk)%pk*inv(c,pk)%pk;
if (p==5 && div) res=res*inv(2,pk)%pk;
return res*(mod/pk)%mod*inv(mod/pk,pk)%mod;
}
inline ll exLucas(ll n,ll m,bool d)
{
return (C(n,m,2,k2,d)+C(n,m,5,k5,d))%mod;
}
inline void pre(int p,ll pk)
{
fac[p!=2][0]=1;
for (int i=1; i<=pk; ++i)
if (i%p) fac[p!=2][i]=fac[p!=2][i-1]*i%pk;
else fac[p!=2][i]=fac[p!=2][i-1];
}
int main()
{
pre(2,512), pre(5,1953125);
ll a,b;
while (~scanf("%lld %lld %lld",&a,&b,&k))
{
mod=Quick_power(10,k,1e9+5), k2=Quick_power(2,k,1e9+5), k5=Quick_power(5,k,1e9+5);
if (a==b) ans=(Quick_power(2,a+b-1,mod)-exLucas(a<<1,a,1)+mod)%mod;
else
{
ans=Quick_power(2,a+b-1,mod);
for (ll i=(a+b)/2+1; i<a; ++i) (ans+=exLucas(a+b,i,0))%=mod;
if ((a+b)%2==0) (ans+=exLucas(a+b,(a+b)/2,1))%=mod;
}
while (ans<mod/10) *IO::fe++=48, mod/=10;
write(ans,'\n');
}
IO::flush();
return 0;
}
template
其实我想借这篇 \(blog\) 来整理一下 \(exLucas\) 。
大意就是让这个东西: \(C_n^m\mod p\) ,其中 \(p\) 不为质数。
而 \(Lucas\) 定理都十分熟知了,和上面一样的式子,不过 \(p\) 为质数,所以可以这样快速求组合数:
\[ Lucas^n_m\equiv Lucas^{n/p}_{m/p}*C^{m\mod p}_{n\mod p}(\mod p) \]
然而现在好像不行了...
并且剧透一下, \(exLucas\) 和 \(Lucas\) 除了名字挺像,要用的东西一点儿关系都没有。
这东西的前置技能是: \(crt\) 和 \(exgcd\) (再次默认都会了)。
开始讨论过程:
我们令 \(P=\prod p_i^{c_i}\) ,那么如果我们知道每个 \(C_n^m\mod p_i^{c_i}\) ,就可以用 \(crt\) 直接合并求解,那怎么来求出这个值呢?
先把 \(C_n^m\) 写成 \(\frac{n!}{m!(n-m)!}\) ,现在我们可以处理阶乘的模。那么如何处理阶乘的模呢?
举个经典例子: \(p=3,n=19,c=2\) 时,我们可以吧式子写成这样:
\[ (19∗18∗17∗16∗15∗14∗13∗12∗11∗10∗9∗8∗7∗6∗5∗4∗3∗2∗1)\\ =(19∗17∗16∗14∗13∗11∗10∗8∗7∗5∗4∗2∗1)∗3^6∗6! \]
我们可以将他分为几个部分 \(19∗(17∗16∗14∗13∗11∗10)∗(8∗7∗5∗4∗2∗1)∗3^6∗6!\)
我们会发现对于每一个整的部分如 \((8∗7∗5∗4∗2∗1)\) 的模数都是一样的,于是这一块我们可以运用快速幂,而剩余的 \(19\) 我们可以进行暴力。
对于 \(6!\) 我们可以继续递归求解,那么怎么分组呢?把每一段的范围定为 \(p^c\) 即可。
\(code\) 放上:
#include<bits/stdc++.h>
typedef long long ll;
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
inline ll Quick_power(ll a,ll b,ll mod)
{
ll ans=1;
while (b)
{
if (b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
if (!b)
{
x=1, y=0;
return ;
}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
}
inline ll inv(ll n,ll m)
{
ll x,y;
exgcd(n,m,x,y);
return (x%m+m)%m;
}
inline ll mul(ll n,ll p,ll pk)
{
if (!n) return 1;
ll ans=1;
for (ll i=1; i<=pk; ++i)
if (i%p) (ans*=i)%=pk;
ans=Quick_power(ans,n/pk,pk);
for (ll i=1; i<=n%pk; ++i)
if (i%p) (ans*=i)%=pk;
return ans*mul(n/p,p,pk)%pk;
}
inline ll C(ll n,ll m,ll p,ll pk)
{
ll zero=0;
for (ll i=n; i; i/=p) zero+=i/p;
for (ll i=m; i; i/=p) zero-=i/p;
for (ll i=n-m; i; i/=p) zero-=i/p;
ll a=mul(n,p,pk), b=mul(m,p,pk), c=mul(n-m,p,pk);
return Quick_power(p,zero,pk)*a%pk*inv(b,pk)%pk*inv(c,pk)%pk;
}
inline ll crt(ll n,ll p,ll pk)
{
return inv(p/pk,pk)*(p/pk)*n;
}
inline ll exLucas(ll n,ll m,ll p)
{
ll tmp=p, ans=0;
for (ll i=2; i*i<=p; ++i)
{
ll k=1;
while (tmp%i==0) k*=i, tmp/=i;
(ans+=crt(C(n,m,i,k),p,k))%=p;
}
if (tmp>1) (ans+=crt(C(n,m,tmp,tmp),p,tmp))%=p;
return ans%p;
}
int main()
{
ll n,m,p;
read(n);read(m);read(p);
write(exLucas(n,m,p),'\n');
IO::flush();
return 0;
}