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;
}
posted @ 2025-08-20 11:29  BaiBaiShaFeng  阅读(6)  评论(0)    收藏  举报
Sakana Widget右下角定位