洛谷P2054 [AHOI2005]洗牌(擴展歐幾裏德)
阿新 • • 發佈:2018-06-08
n+1 line spl .html swa 觀察 推出 exgcd fine
洛谷題目傳送門
來個正常的有證明的題解
我們不好來表示某時刻某一個位置是哪一張牌,但我們可以表示某時刻某一張牌在哪個位置。
設數列\(\{a_{i_j}\}\)表示\(i\)號牌經過\(j\)次洗牌後的位置,我們試著來遞推一下
首先,如果此刻牌在上面一疊,顯然\(a_{i_{j+1}}=2a_{i_j}\)
接著,如果這張牌在下面一疊,那麽\(a_{i_{j+1}}=2(a_{i_j}-\frac n2)-1=2a_{i_j}-(n+1)\),應該也很好推出來
寫在一起,觀察一下
\[a_{i_{j+1}}=\begin{cases}2a_{i_j}\qquad\qquad\ ,a_{i_j}\leq\frac n2\\2a_{i_j}-(n+1),a_{i_j}>\frac n2\end{cases}\]
誒,兩個式子都有一個系數\(2\)呢!那我們可不可以把它看成模\(n+1\)意義下的結果呢?
於是可以進一步得到\(a_{i_j}\equiv2^mi(\mod n+1)\)(\(i\)就是\(a_{i_0}\))
題目已經知道了\(a_{i_j}\),來求\(i\),不就是一個線性同余不定方程麽?exgcd搞一下就好啦!因為\(\gcd(2^m,n+1)=1\),所以根本不用像青蛙的約會那樣麻煩。
看樓上大佬用異或寫swap?!第一次見的蒟蒻表示只能Orz。然後蒟蒻就把exgcd這個函數成功壓縮到了一行。。。。。。
#include<cstdio>
#define LL long long
LL n,m,l,x=1,y;
LL qpow(LL b,LL k){//快速冪求2^m
LL a=1;
while(k){
if(k&1)a*=b,a%=n+1;
b*=b,b%=n+1;
k>>=1;
}
return a;
}
void exgcd(LL a,LL b){//一行exgcd233
if(b)exgcd(b,a%b),(y^=x^=y^=x)-=a/b*x;
}
int main(){
scanf("%lld%lld%lld",&n,&m,&l);
exgcd(qpow(2 ,m),n+1);
printf("%lld\n",(l*x%(n+1)+n+1)%(n+1));//註意化成最小正整數
return 0;
}
洛谷P2054 [AHOI2005]洗牌(擴展歐幾裏德)