洛谷——P2054 [AHOI2005]洗牌(扩展欧几里得,逆元)

P2054 [AHOI2005]洗牌

 

扩展欧拉定理求逆元

$1 2 3 4 5 6$
$4 1 5 2 6 3$
$2 4 6 1 3 5$
$1 2 3 4 5 6$

手推一下样例,你就会发现是有规律的:

 

位置->位置

$1->2$

$2->4$

$3->6$

$4->1$

$5->3$

$6->5$

 

规律显然,位于位置$x$的数,进行一次洗牌操作位置就会变成$x*2\%(n+1)$

那么位于$x$的数,经过$m$次操作,位置就会变成$x*2^m\%(n+1)$

那么可以列出一下同余方程

$x*2^m≡k(mod(n+1))$

 

然后就比较显然了,只有一个未知数$x$,扩展$gcd$好了,=_=,博主太蒟,没有看懂

另一种解释方法是:

变换一下得:$x≡k*{2^{m}}^{-1}(mod(n+1))$

问题就转换成了求解${2^{m}}$在$\%(n+1)$意义下的逆元,还是$exgcd$

 

$ans={2^{m}}^{-1}*l\%(n+1)$

#include<iostream>
#include<cstdio>

#define LL long long 
using namespace std;


LL n,m,l;
LL pow(LL a,LL b){
    LL s=1;
    for(;b;b>>=1,a=a*a%(n+1))
        if(b&1) s=s*a%(n+1);
    return s;
}

LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){x=1,y=0;return a;}
    LL gc=exgcd(b,a%b,x,y);
    LL tmp=x;x=y;y=tmp-a/b*y;
    return gc;
}

int main()
{
    scanf("%lld%lld%lld",&n,&m,&l);
    LL m_2=pow(2,m);
    LL x,y;
    exgcd(m_2,n+1,x,y);
    
    printf("%lld\n",(x*l%(n+1)+(n+1))%(n+1));
    
    return 0;
}

 

posted @ 2018-10-10 16:56  清风我已逝  阅读(234)  评论(0编辑  收藏  举报