P2054 [AHOI2005] 洗牌
一道煞笔题,让我写了半个上午,尤其是那个垃圾样例图片,让我疑惑了半天...... 还是手摸出来的。
题意概括:
一次洗牌:将一叠 \(N\)(\(N\)为偶数)张扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。
给了三个数: \(N,M,L\)。
求解洗牌 \(M\) 次,第 \(L\) 张牌是哪一个。
\(N,M \le 1\times10^{10}\)
咋做:
我们发现了这个奇葩的垃圾数据范围,这就说明一定有什么不为人知的规律或者性质,我们开始超级手搓。
我们发现上边的一叠很明显:\(i\to 2i\)。
下边的一叠比较难搞,但是我们把每一个 \(2i\) 写出来,发现这个位置在正常的位置就是 \(\mod(n+1)\)。
然后呢?我们不会了,但是发现这个问题就是求一个未知数,我们大胆尝试 Exgcd。
列出式子拆一下 \(\mod\) 运算。
\(2^m x + (n+1) y = L\)
就直接搞,记得用龟速乘,因为模数有可能大于 \(1e9\) 级别,所以需要这么搞,劳资调了半天没发现。
代码
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace Math{
int mul(int a, int b, int mod){
int sum=0;
while(b){
if(b&1) sum=(sum+a)%mod;
a=(a+a)%mod; b>>=1;}
return sum;
}
int quick_power(int a, int b, int mod){
int res=1;
while(b){
if(b&1) res=(mul(res,a,mod))%mod;
a=(mul(a,a,mod))%mod; b>>=1;}
return res;
}
int Exgcd(int a, int b, int &x, int &y){
if(!b){x=1, y=0; return a;}
int Gcd=Exgcd(b,a%b,y,x);
y-=x*(a/b); return Gcd;
}
int Gcd(int a, int b){
if(!b) return a;
return Gcd(b,a%b);
}
}
namespace BaiBaiShaFeng{
int n, m, l;
void solve(){
cin>>n>>m>>l; int a, b, x, y, Gcd;
a=Math::quick_power(2,m,n+1); b=n+1;
Gcd=Math::Exgcd(a,b,x,y);
int xlen=b/Gcd, x_0;
x_0=Math::mul(x,l/Gcd,xlen);
cout<<(x_0%xlen+xlen)%xlen<<'\n';
}
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
BaiBaiShaFeng::solve();
return 0;
}

浙公网安备 33010602011771号