洛谷P2054 [AHOI2005]洗牌(扩展欧几里德)

洛谷题目传送门

来个正常的有证明的题解

我们不好来表示某时刻某一个位置是哪一张牌,但我们可以表示某时刻某一张牌在哪个位置。

设数列({a_{i_j}})表示(i)号牌经过(j)次洗牌后的位置,我们试着来递推一下

首先,如果此刻牌在上面一叠,显然(a_{i_{j+1}}=2a_{i_j})

接着,如果这张牌在下面一叠,那么(a_{i_{j+1}}=2(a_{i_j}-frac n2)-1=2a_{i_j}-(n+1)),应该也很好推出来

写在一起,观察一下

[a_{i_{j+1}}=egin{cases}2a_{i_j}qquadqquad ,a_{i_j}leqfrac n2\2a_{i_j}-(n+1),a_{i_j}>frac n2end{cases} ]

诶,两个式子都有一个系数(2)呢!那我们可不可以把它看成模(n+1)意义下的结果呢?

于是可以进一步得到(a_{i_j}equiv2^mi(mod n+1))(i)就是(a_{i_0})

题目已经知道了(a_{i_j}),来求(i),不就是一个线性同余不定方程么?exgcd搞一下就好啦!因为(gcd(2^m,n+1)=1),所以根本不用像青蛙的约会那样麻烦。

看楼上大佬用异或写swap?!第一次见的蒟蒻表示只能Orz。然后蒟蒻就把exgcd这个函数成功压缩到了一行。。。。。。

#include<cstdio>
#define LL long long
LL n,m,l,x=1,y;
LL qpow(LL b,LL k){//快速幂求2^m
	LL a=1;
	while(k){
		if(k&1)a*=b,a%=n+1;
		b*=b,b%=n+1;
		k>>=1;
	}
	return a;
}
void exgcd(LL a,LL b){//一行exgcd233
	if(b)exgcd(b,a%b),(y^=x^=y^=x)-=a/b*x;
}
int main(){
	scanf("%lld%lld%lld",&n,&m,&l);
	exgcd(qpow(2,m),n+1);
	printf("%lld
",(l*x%(n+1)+n+1)%(n+1));//注意化成最小正整数
	return 0;
}
原文地址:https://www.cnblogs.com/flashhu/p/9152921.html