1 条题解

  • 0
    @ 2025-8-24 21:52:04

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar shadowice1984
    これが勘違いでも、これが勘違いでも

    搬运于2025-08-24 21:52:04,当前版本为作者最后更新于2018-05-14 20:09:30,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    过气数论魔法……

    看了下网上的扩展lucas教程好像写的不是非常清楚……

    反正我一开始是绕了不少弯之后才明白怎么写的……

    所以就用这道题当作扩展lucas的板子吧……


    本题题解

    首先我们把小A抛硬币的胜负情况和小B抛硬币的胜负情况看成两个01序列,然后我们把这两个01序列接起来就形成了一个长度为a+b的01序列

    现在我们将原问题转化为了对01序列计数的问题

    以下设小a的序列中有WaW_{a}个1,小b的序列中有WbW_{b}个1

    然后我们先考虑一个比较简单的情况a==ba==b

    那么我们假设现在有一个胜的01序列,那么一定满足条件Wa>WbW_{a}>W_{b},由a==ba==b立即推得aWa>bWba-W_{a}>b-W_{b},因此我们证明了任意一个胜的01序列01反转之后一定是一个负的序列

    而显然平的序列01反转之后依然是平的01序列,那么我们其实只需要计算出总方案,之后减去平的方案数,最后除以2即可了……

    那么我们计算平的01序列可以直接暴力枚举这个01序列中有多少1来计数,式子长这样

    i=1a(Cai)2=C2aa\sum_{i=1}^{a}(C_{a}^{i})^{2}=C_{2a}^{a}

    这个式子的证明下面会讲

    下面我们来讨论略微复杂一点的情形,a>ba>b

    我们还是考虑01反转这个套路,但这时我们发现当Wa>WbW_{a}>W_{b}时我们只能推出Wa<Wb-W_{a}<-W_{b}但是由于a>ba>b因此我们无法比较aWaa-W_{a}bWbb-W_{b}的大小,此时的情况略微的有点辣手……

    但是我们反过来考虑呢?考虑一个负或者平的序列,当Wa<=WbW_{a}<=W_{b}的时候我们可以推出Wa>=Wb-W_{a}>=-W_{b}又因为a>ba>b因此我们可以立即推出aWa>bWba-W_{a}>b-W_{b}换句话说,一个负或者平的01序列反转之后一定是一个胜的01序列

    因此我们发现只有部分胜的01序列反转之后还是胜的01序列,换句话来讲,只有这部分01序列没有“配对”,因此如果我们在总方案里加上这部分01序列,那么所有的胜的01序列均可以配对,此时只需要将方案数除2就是胜的01序列方案

    那么我们需要找到一个条件使得胜的01序列反转之后仍然是一个胜的01序列

    我们可以列出一个不等式组

    Wa>WbW_{a}>W_{b}aWa>bWba-W_{a}>b-W_{b}

    我们可以解出这样一个比较优美的形式

    ab>WaWb>0a-b>W_{a}-W_{b}>0

    那么我们呢就可以通过枚举WbW_{b}以及WaWbW_{a}-W_{b}来计数了……式子大概长这样

    $\sum_{i=0}^{b}\sum_{j=1}^{a-b-1}C_{b}^{i}C_{a}^{i+j}$

    我们交换一下Σ,即swap(i,j)得到

    $\sum_{i=1}^{a-b-1}\sum_{j=0}^{b}C_{b}^{j}C_{a}^{i+j}$

    $=\sum_{i=1}^{a-b-1}\sum_{j=0}^{b}C_{b}^{b-j}C_{a}^{i+j}$

    观察到bj+i+j=b+ib-j+i+j=b+i因此我们可以重写内层Σ

    i=1ab1j+k=b+iCbjCak\sum_{i=1}^{a-b-1}\sum_{j+k=b+i}C_{b}^{j}C_{a}^{k}

    这个形式被称为范德蒙德卷积,即以下等式恒成立

    i+j=kCaiCbj=Ca+bk\sum_{i+j=k}C_{a}^{i}C_{b}^{j}=C_{a+b}^{k}

    所以我们的式子被化成了

    i=1ab1Ca+bb+i\sum_{i=1}^{a-b-1}C_{a+b}^{b+i}

    (备注:根据范德蒙德卷积我们也可以证明

    $\sum_{i=1}^{a}(C_{a}^{i})^{2}=\sum_{i=1}^{a}C_{a}^{i}C_{a}^{a-i}=C_{2a}^{a}$

    这个式子是成立的)

    然后我们加上一个方案数然后除以2的话我们可以得到最后的答案式子

    A==BA==B

    Ans=2A+BC2aa2Ans=\frac{2^{A+B}-C_{2a}^{a}}{2}

    A>BA>B

    $Ans=\frac{2^{A+B}+\sum_{i=1}^{A-B-1}C_{A+B}^{B+i}}{2}$

    然后问题来了,我们怎么计算Cnmmod109C_{n}^{m}mod10^9?

    如果mod的是一个不大的质数,那么我们可以通过lucas定理轻松的求解,但是如果p不是质数呢?此时我们需要一种被称之为扩展lucas的上古魔法来帮助我们求解这类问题


    拓展Lucas

    首先由于我们的模数是任意的,我们将模数进行唯一分解,分解成PkP^{k}p为素数的连乘积形式,此时我们只需要分别计算出CnmmodpkC_{n}^{m}modp^{k}最后使用中国剩余定理解同余方程就行了

    问题来了怎么求出CnmmodpkC_{n}^{m}modp^{k}呢?

    此时网上的教程会告诉你我们只需要求出N!modpkN!modp^{k}最后除在一起就好了……

    但是问题来了,分母有极大的概率是pkp^{k}的倍数,此时不存在乘法逆元

    因此我们需要略微费点功夫,我们将N!N!表示为a×Pba×P^{b}的形式

    然后计算除法的时候各个阶乘的a部分乘法逆元都存在,然后在b部分运行指数上的加减法就好了

    最后判以下指数是否大于k如果大于k直接输出0即可,否则就合并两个部分的值就行了

    最后的问题,如何求出N!modPkN!modP^{k}的不含有因子p的部分呢?

    我们举一个非常老套的例子,计算19!mod3219!mod3^2

    把阶乘一字拆开得到

    1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×191×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19

    然后提走所有含有因子3的部分一个3式子变成了

    6!×36×1×2×4×5×7×8×10×11×13×14×16×17×196!×3^{6}×1×2×4×5×7×8×10×11×13×14×16×17×19

    然后我们呢将后边的部分膜上一个323^2得到了

    6!×36×(1×2×4×5×7×8)2×196!×3^{6}×(1×2×4×5×7×8)^{2}×19

    让我们来观察各个部分的特征,左侧的阶乘可以直接递归的去处理,363^{6}可以单独计算,而中间的部分其实是一个伪阶乘的形式,即去掉了3的倍数的阶乘,发现最长不超过pkp^{k}可以打表预处理,而最后剩下的19其实是一个未完的周期,已经在我们的表当中了,因此如果记得伪阶乘为BiB_{i}那么我们可以得到这样的递归式来求阶乘的有乘法逆元的部分

    $lucas(n,p^{k})=lucas(\lfloor \frac{n}{p}\rfloor ,p^{k})B_{p^{k}-1}^{\lfloor \frac{n}{p^{k}} \rfloor}B_{n\%p^{k}}$

    其中预处理是O(pk)O(p^k)的,每次询问阶乘是O(log2n)O(log^{2}n)的(计入了快速幂的log)

    最后使用中国剩余定理解同余方程即可得到C(n,m)modpC(n,m)modp

    最后一个问题,2在膜10910^9的意义下乘法逆元是不存在的,我们该怎么除2呢?

    我们发现如果a+ba+b是奇数,那么我们需要计算的式子其实是杨辉三角中间对称的几列,那么我们只计算左边的几列或者右边的几列就可以除2了,但是在a+b为偶数的时候我们会发现剩下了一个Ca+b(a+b)/2C_{a+b}^{(a+b)/2}情况会有点辣手

    但是我们注意到根据帕斯卡恒等式(杨辉三角)可以得到

    $C_{2a}^{a}=C_{2a-1}^{a}+C_{2a-1}^{a-1}=2C_{2a-1}^{a}$

    于是我们计算Ca+b1(a+b)/2C_{a+b-1}^{(a+b)/2}就可以了

    上代码~

    #include<cstdio>
    #include<algorithm>
    using namespace std;typedef unsigned long long ll;const ll mod[2]={512,1953125};const ll bs[2]={2,5};
    const ll M=1e9;//省事起见直接mod1e9了 
    inline ll po(ll a,ll p,ll mod){ll r=1;for(;p;p>>=1,a=a*a%mod)if(p&1)r=r*a%mod;return r;}
    inline void exgcd(ll a,ll& x,ll b,ll& y)//乘法逆元和快速幂 
    {if(b==0){x=1;y=0;return;}exgcd(b,x,a%b,y);ll tp=x;x=y;y=tp-a/b*y;}
    inline ll inv(ll a,ll b){ll x;ll y;exgcd(a,x,b,y);return (x+b)%b;}
    ll bru[2][1953200];ll a;ll b;int k;int nb[15];
    ll lucas(ll n,ll t)//一行lucas 
    {return n?lucas(n/bs[t],t)*bru[t][n%mod[t]]%mod[t]*po(bru[t][mod[t]-1],n/mod[t],mod[t])%mod[t]:1;}
    inline ll c(ll n,ll m,int t)
    {
    	ll d=0;for(ll i=n;i;i/=bs[t])d+=i/bs[t];
    	for(ll i=m;i;i/=bs[t])d-=i/bs[t];
    	for(ll i=n-m;i;i/=bs[t])d-=i/bs[t];if(d>=9){return 0;}//剪枝,我们直接暴力计算出指数然后判断是否是0 
    	ll p1=lucas(n,t);ll p2=lucas(m,t);ll p3=lucas(n-m,t);//计算非互质部分 
    	return po(bs[t],d,mod[t])*p1%mod[t]*inv(p2,mod[t])%mod[t]*inv(p3,mod[t])%mod[t];//乘上p的幂次 
    }
    inline void prit(ll x)//打印函数 
    {
    	for(int i=1;i<=10;i++)nb[i]=0;
    	for(int i=1;x;i++,x/=10)nb[i]=x%10;
    	for(int i=k;i>=1;i--)printf("%d",nb[i]);printf("\n");
    }
    int main()//打表伪阶乘 
    {
    	bru[0][0]=1;for(int i=1;i<mod[0];i++){bru[0][i]=(i%2)?i*bru[0][i-1]%mod[0]:bru[0][i-1];}
    	bru[1][0]=1;for(int i=1;i<mod[1];i++){bru[1][i]=(i%5)?i*bru[1][i-1]%mod[1]:bru[1][i-1];}
    	while(scanf("%lld%lld%d",&a,&b,&k)!=-1)
    	{
    	 	ll ret2=0;ll ret5=0;
    	 	if(a==b)//特判a==b 
    	 	{
    	 		ret2=c(2*a-1,a,0);ret5=c(2*a-1,a,1);
    	 		ret2=ret2*mod[1]%M*inv(mod[1]%mod[0],mod[0])%M;
    	 		(ret2+=ret5*mod[0]%M*inv(mod[0],mod[1]))%=M;ret2=M-ret2;
    	 		(ret2+=po(2,a+b-1,M))%=M;prit(ret2);//中国剩余定理解同余方程 
    	 	}
    	 	else 
    	 	{
    	 		if((a+b)%2)//如果a+b是奇数 
    	 		{
    	 			for(ll i=b+1;i<=(a+b)/2;i++)(ret2+=c(a+b,i,0))%=mod[0];
    				for(ll i=b+1;i<=(a+b)/2;i++)(ret5+=c(a+b,i,1))%=mod[1];//只算一半 
    	 			ret2=ret2*mod[1]%M*inv(mod[1]%mod[0],mod[0])%M;
    	 			(ret2+=ret5*mod[0]%M*inv(mod[0],mod[1]))%=M;(ret2+=po(2,a+b-1,M))%=M;
    	 			prit(ret2);
    	 		}
    	 		else //偶数的时候通过计算C(a+b-1,(a+b)/2)来将C(a+b,(a+b)/2)除2 
    	 		{
    	 			for(ll i=b+1;i<(a+b)/2;i++)(ret2+=c(a+b,i,0))%=mod[0];(ret2+=c(a+b-1,(a+b)/2,0))%=mod[0];
    	 			for(ll i=b+1;i<(a+b)/2;i++)(ret5+=c(a+b,i,1))%=mod[1];(ret5+=c(a+b-1,(a+b)/2,1))%=mod[1];	 			
    	 			ret2=ret2*mod[1]%M*inv(mod[1]%mod[0],mod[0])%M;
    	 			(ret2+=ret5*mod[0]%M*inv(mod[0],mod[1]))%=M;(ret2+=po(2,a+b-1,M))%=M;
    	 			prit(ret2);
    	 		}
    	 	}
    	}return 0;//拜拜程序~ 
    }
    
    
    • 1

    信息

    ID
    1396
    时间
    1000ms
    内存
    125MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者