[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;
}
posted @ 2020-08-10 12:41  Creed-qwq  阅读(98)  评论(0编辑  收藏  举报