1. 程式人生 > >【BZOJ1965/Ahoi2005】SHUFFLE 洗牌

【BZOJ1965/Ahoi2005】SHUFFLE 洗牌

解析:

  法一:找規律   手玩樣例可以發現原位置為pp的數經過mm次洗牌後位置變為:p2mmod(n1)p*2^mmod(n-1)   法二:稍微嚴謹的數學證明   如果當前位置為pp變化一次後到哪個位置,存在兩種情況:   1.pn2p\le\frac{n}{2}   易知變到第2p2p個位置。   2.n2<pn\frac{n}{2}<p\le n   易知變到第(pn/2)21=2pn1(pn/2)21=2p(n+1)(p−n/2)∗2−1=2p−n−1(p−n/2)∗2−1=2p−(n+1)

個位置。   前面顯然有2pn2p\le n。   後面有pn/2+1=>2pn+2=>2p>n+1p\ge n/2+1=>2p\ge n+2=>2p > n+1。   所以我們可以認為每次操作之後都由pp位置變成了2lmod(n+1)2lmod(n+1)位置。   所以原問題變為求解x2mL(mod(n1))x*2^m\equiv L(mod(n-1))
,轉換一下得到:xL/2m(mod(n1))x\equiv L/2^m(mod(n-1))   注意到2m2^mn+1n+1是互質的。所以直接用擴歐求逆元再用快速冪求解即可。

程式碼:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,m,l,mod,x,y;

inline int mul(int a,int b){int ans=0;while(b){if(b&1)ans=(ans+a)%mod;b>>=
1,a=(a+a)%mod;}return ans;} inline int ksm(int a,int b){int ans=1;while(b){if(b&1)ans=mul(ans,a);b>>=1,a=mul(a,a);}return ans;} inline void exgcd(int a,int b) { if(!b) x=1,y=0; else { exgcd(b,a%b); int t=x; x=y,y=t-a/b*x; } } signed main() { cin>>n>>m>>l,mod=n+1; exgcd(ksm(2,m),mod); x=(x%mod+mod)%mod; //若不轉成最小正整數解可能是負數快速乘會T! cout<<mul(l,x)<<"\n"; return 0; }