[AHOI2005]洗牌
链接:https://www.luogu.com.cn/problem/P2054
设 n=2m
为了方便进行有关剩余系的推导
把原本的标号
1.....2m
改为
0......2m-1
分类讨论一下
原本在位置x的牌
经过一次洗牌后
会出现在什么位置
这个式子还是比较好推的
分类讨论可得
\[\begin{align*}
x<m &: y= 2x + 1
\\
x\ge m &: y= 2x - 2m
\end{align*}
\]
然后用一些数学技巧处理一下
\[\begin{align*}
x<m &: y= 2x + 1
\\
x\ge m &: y= 2x + 1 -(2m+1)
\end{align*}
\]
然后发现这个式子就等价于
\[y\equiv{ 2x + 1 } \pmod {2m+1}
\]
用一些简单的数列技巧处理
\[y+1\equiv{ 2(x + 1) } \pmod {2m+1}
\]
所以
\[X_n\equiv{X_0 * 2^M} \pmod {2m+1}
\]
然后问题就转化为一个解同余方程了
恶心的地方在于这个题目的数据范围比较恶心
会爆long long
所以得用一下龟速乘法
#include<bits/stdc++.h>
#define N 110000
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
inline ll read()
{
char ch=0;
ll x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
return x*flag;
}
ll mo;
ll ksc(ll x,ll y,ll p){return ((x*y-(ll)(((ldb)x*y+0.5)/p)*p)%p+p)%p;}
ll ksm(ll x,ll k)
{
ll ans=1;
while(k)
{
if(k&1)ans=ksc(ans,x,mo);
k>>=1;x=ksc(x,x,mo);
}
return ans;
}
ll X,Y;
ll exgcd(ll a,ll b)
{
if(!b){X=1;Y=0;return a;}
ll d=exgcd(b,a%b),t=X;
X=Y;Y=t-(a/b)*Y;
return d;
}
int main()
{
ll n=read(),m=read(),l=read();mo=n+1;
ll a=ksm(2,m),b=mo,d=exgcd(a,b),p1=b/d;
X=(X%p1+p1)%p1;X=ksc(X,(l/d),p1);
printf("%lld",X);
return 0;
}