【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;
}
QQ40523591~欢迎一起学习交流~