1. 程式人生 > >bzoj1965 [Ahoi2005]洗牌

bzoj1965 [Ahoi2005]洗牌

width gist ges open c++ urn col div eight

Description

為了表彰小聯為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張撲克牌的牌面大小是多少的科學家得勝。小聯想贏取遊戲的勝利,你能幫助他嗎?

Input

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

Output

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

Sample Input

6 2 3

Sample Output

6

正解:快速冪。

首先這是一個置換。

然後我們可以打表發現,它置換以後的數在模$n+1$意義下乘$2$就是它自己。

於是用一個快速冪,再求一個逆元就行了。

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 
 6 using namespace std;
 7 
 8 ll n,m,l,rhl;
 9 
10 il void exgcd(RG ll a,RG ll b,RG ll &x,RG ll &y){
11 if (!b){ x=1,y=0; return; } exgcd(b,a%b,y,x); 12 y-=a/b*x; return; 13 } 14 15 il ll inv(RG ll v){ 16 RG ll x,y; exgcd(v,rhl,x,y); 17 while (x<0) x+=rhl; return x; 18 } 19 20 il ll qpow(RG ll a,RG ll b){ 21 RG ll ans=1; 22 while (b){ 23 if (b&1) ans=ans*a%rhl; 24 a=a*a%rhl,b>>=1; 25 } 26 return ans; 27 } 28 29 il ll qmul(RG ll a,RG ll b){ 30 RG ll ans=0; 31 while (b){ 32 if (b&1) ans=(ans+a)%rhl; 33 a=(a<<1)%rhl,b>>=1; 34 } 35 return ans; 36 } 37 38 int main(){ 39 #ifndef ONLINE_JUDGE 40 freopen("shuffle.in","r",stdin); 41 freopen("shuffle.out","w",stdout); 42 #endif 43 cin>>n>>m>>l,rhl=n+1; 44 RG ll bin=qpow(2,m); 45 cout<<qmul(l,inv(bin)); 46 return 0; 47 }

bzoj1965 [Ahoi2005]洗牌