【AHOI2005】洗牌(乘法逆元+exgcd)

传送门

先来找一发规律

先把6张牌洗出来的各种顺序依次写下来
1 2 3 4 5 6
4 1 5 2 6 3
2 4 6 1 3 5
1 2 3 4 5 6
....

发现是有周期性的,所以我们就看前三排好了

我们试图观察一下每个数字的走向

1:先是右移1 然后2 然后3
2:先是右移2 然后4 然后8
3:3,6,9....
....
(似乎这个还不是很直观 可以画个更大的 8张牌)

我们发现 如果设当前洗到了第m次牌 并且设x是要求的牌的牌面大小x的话

可得 x*(2^m)%(n+1)=L

就是同余方程 x*(2^m) = L (mod n+1)

然后把2^m移到右边 变成2^m在模n+1意义下的逆元

就是x = L*此逆元 (mod n+1)

然后右边的全部知道 就可以算出x了

(注意边模边算快速幂)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,l;
ll quickpow(ll x,ll y)
{
	ll base=x,ans=1;
	while(y)
	{
		if(y&1)	ans=(ans*base)%(n+1);
		base=(base*base)%(n+1);
		y>>=1;
	}	
	return ans;
}
inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	ll r=exgcd(b,a%b,x,y);
	ll temp=x;
	x=y;
	y=temp-a/b*y;
	return r;
}
int main()
{
	cin>>n>>m>>l;
	ll powm_2=quickpow(2,m);
	ll x,y;
	exgcd(powm_2,n+1,x,y);
	ll ny=(x%(n+1)+n+1)%(n+1);
	cout<<(ny*l)%(n+1)<<'\n';
	return 0;
} 
posted @ 2018-09-26 23:47  Patrickpwq  阅读(132)  评论(0编辑  收藏  举报