除法逆元(ZS and The Birthday Paradox,cf 711E)

来源:互联网 发布:认知心理学 知乎 编辑:程序博客网 时间:2024/05/01 07:06
这题真挺不错的,需要一些数学知识,对细节要求高。

一些资料与参考:

除法逆元 http://blog.csdn.net/acmmaxx/article/details/18409701

逆元详解 http://blog.csdn.net/acdreamers/article/details/8220787

参考代码 http://blog.csdn.net/miracle_ma/article/details/52383461



寻找一共有多少个2的因子时用的方法不错。

不能写成pow,要写成mypow。

最后输出分子时要多次取模防止出现负数。



当a为正整数,m为素数,而且a与m互素时,a模m的逆元为a^(m-2)模m。

此题中a为2,m为一个素数。显然2与m互素。代入公式直接求逆元。

求逆元是为了使用除法逆元公式。

( a/b ) % c == ( a*b1 ) % c==((a%c)*(b1%c))%c

其中b1是b模c的逆元。




在计算过程中需要求分子分母gcd模m的逆元。

带入公式,即2^(cnt*(m-2))%m的值。

难道直接mypow(mypow(2,cnt),m-2)%m来算吗?

其实完全可以这样算,几乎不会更慢。但这个表达式是可以化简的。

根据费马小定理,2^(m-1)%m==1。

所以:2^(cnt*(m-2))%m=(2^(m-1)%m  *  2^(m-1)%m  *  ...... * 2^(m-1)%m * 2^(cnt*(m-2)%(m-1)))%m=2^(cnt*(m-2)%(m-1))%m。

乘法取模,化简,乘开来,化简,得:2^(-cnt%(m-1))%m,。

根据费马小定理,以及乘法取模上式=(2^(-cnt%(m-1))*2^(m-1))%m=2^(m-1-tmp%(m-1))。

这就是代码中aaa的由来。


#include<bits/stdc++.h>using namespace std;typedef long long ll;const ll mod=1000003;ll n,k;ll mypow(ll x,ll n){    ll ret=1;    while(n)    {        if(n&1)        {            ret=(ret*x)%mod;        }        x=(x*x)%mod;        n>>=1;    }    return ret;}int main(){    scanf("%I64d %I64d",&n,&k);    ll x=0;    while((1ll<<x)<k) x++;    if(x>n)    {        puts("1 1");        return 0;    }    ll cnt=0;    for(ll i=2;i<k;i<<=1)    {        cnt+=(k-1)/i;//这个方法好,一开始在这里超时了。我还一个个的找。    }    ll aaa=mypow(2,mod-1-cnt%(mod-1));//用mypow不能用pow,aaa化简了,但并没有快多少。    ll fm=(mypow(mypow(2,n),k-1)*aaa)%mod;    if(k-1>=mod) printf("%I64d %I64d\n",fm,fm);    else    {        ll fz=1;        ll da=mypow(2,n);        for(ll i=1;i<k;i++)            fz=(fz*(da-i))%mod;        fz=(fz*aaa)%mod;        printf("%I64d %I64d\n",((fm-fz)%mod+mod)%mod,fm);//小细节,为了防止输出负数。    }    return 0;}

0 0