1. 程式人生 > >AHOI 2005--洗牌(擴展歐幾裏得&快速冪)

AHOI 2005--洗牌(擴展歐幾裏得&快速冪)

pre -- esp 多少 src ima 遊戲 n+1 進行

發現BZOJ上也是有不少水題的哦!

排名進前3000也是很容易的哦!

然後就要繼續刷題了哦!

題意

為了表彰小聯為Samuel星球的探險所做出的貢獻,小聯被邀請參加Samuel星球近距離載人探險活動。

由於Samuel星球相當遙遠,科學家們要在飛船中度過相當長的一段時間,小聯提議用撲克牌打發長途旅行中的無聊時間。玩了幾局之後,大家覺得單純玩撲克牌對於像他們這樣的高智商人才來說太簡單了。有人提出了撲克牌的一種新的玩法。

對於撲克牌的一次洗牌是這樣定義的,將一疊N(N為偶數)張撲克牌平均分成上下兩疊,取下面一疊的第一張作為新的一疊的第一張,然後取上面一疊的第一張作為新的一疊的第二張,再取下面一疊的第二張作為新的一疊的第三張……如此交替直到所有的牌取完。

如果對一疊6張的撲克牌1 2 3 4 5 6,進行一次洗牌的過程如下圖所示:

技術分享

從圖中可以看出經過一次洗牌,序列1 2 3 4 5 6變為4 1 5 2 6 3。當然,再對得到的序列進行一次洗牌,又會變為2 4 6 1 3 5。

遊戲是這樣的,如果給定長度為N的一疊撲克牌,並且牌面大小從1開始連續增加到N(不考慮花色),對這樣的一疊撲克牌,進行M次洗牌。最先說出經過洗牌後的撲克牌序列中第L張撲克牌的牌面大小是多少的科學家得勝。小聯想贏取遊戲的勝利,你能幫助他嗎?

輸入格式:

輸入文件中有三個用空格間隔的整數,分別表示N,M,L。(其中0<N≤10^10 ,0 ≤M≤10^10,且N為偶數)。

輸出格式:

單行輸出指定的撲克牌的牌面大小。

Solution

差點看成置換群的題目。。。結果只是道水題。。。

容易看出每張牌的位移方式是一樣的。。。

設某張牌在N張牌中初始位置在x,那麽一次洗牌後位置就變為(2*x)%(N+1);

那樣就能得到方程:x*(2^M) Ξ L (mod N+1)

其中(2^M)可以快速冪取模,於是又得到新方程:x*k Ξ L (mod N+1);

典型的擴展歐幾裏得。。。

代碼

#include<cstdio>
#define LL long long
using namespace std;
LL d=0,x=0,y=0;
LL pow(LL a,LL b,LL mod){
    LL s=1;
    while(b){
        if(b&1) s=(s*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return s;
}
void gcd(LL a,LL b,LL &d,LL &x,LL &y){
    if (b==0){
        d=a;
        x=1;
        y=0;
        return ;
    }
    gcd(b,a%b,d,y,x);
    y-=a/b*x;
    return;
}
int main(){
    LL n,m,l,k;
    scanf("%lld%lld%lld",&n,&m,&l);
    n++;
    k=pow(2,m,n);
    gcd(k,n,d,x,y);
    while(x<0) x+=n;
    d=(l/d)*x%n;
    printf("%lld",d);
    return 0;
}

This passage is made by Yukino.

AHOI 2005--洗牌(擴展歐幾裏得&快速冪)