luogu2054 洗牌 同余方程
Posted headboy2002
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu2054 洗牌 同余方程相关的知识,希望对你有一定的参考价值。
题目大意
对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。
如果对一叠6张的扑克牌1 2 3 4 5 6,进行一次洗牌的过程如下图所示:
如果给定长度为N的一叠扑克牌,并且牌面大小从1开始连续增加到N(不考虑花色),对这样的一叠扑克牌,进行M次洗牌。说出经过洗牌后的扑克牌序列中第L张扑克牌的牌面大小是多少。
思路
我们看看一张位于位置p扑克牌洗一次后的位置p‘在哪里。若p<=N/2,这张扑克牌就到了第p对牌中的第2张,位置为p*2;若p>N/2,这张扑克牌就到了第p-N/2对牌中的第一张,故p‘=(p-N/2)*2-1=p*2-(N+1)。因为p<=N/2时p*2%(N+1)=p*2,所以综上所述,p‘=p*2%(N+1)。洗m次,即令运算*2%(N+1)进行m次,2便乘了m次,模了m遍N+1与只模一次的效果是相同的。综上所述,洗m次后牌移动到了位置p*2^m%(N+1)。现在给出最终的位置l,那么就是让我们解同余方程x*2^m≡l(mod N+1)。利用快速幂求2^m,然后解方程模板代入即可。
#include <cstdio> #include <cstring> using namespace std; #define ll long long ll Mult(ll a, ll b, ll p) { ll ans = 0; while (b) { if (b & 1) ans = (ans + a) % p; a = (a+a)%p; b >>= 1; } return ans; } ll Power(ll a, ll n, ll p) { ll ans = 1; while (n) { if (n & 1) ans = Mult(ans, a, p); a = Mult(a, a, p); n >>= 1; } return ans; } ll Exgcd(ll a, ll b, ll &x, ll &y) { if (b == 0) { x = 1; y = 0; return a; } ll d = Exgcd(b, a%b, x, y); ll tx = x; x = y; y = tx - (a / b) * y; return d; } ll Gcd(ll a, ll b) { return b ? Gcd(b, a%b) : a; } ll Eq(ll a, ll b, ll m) { ll gcd = Gcd(a, m); if (b%gcd) return -1; ll x, y; Exgcd(a, m, x, y); x = x * b / gcd; ll p = m / gcd; return (x%p+p) % p; } int main() { #ifdef _DEBUG freopen("c:\\noi\\source\\input.txt", "r", stdin); #endif ll n, m, l; scanf("%lld%lld%lld", &n, &m, &l); printf("%lld\n", Eq(Power(2, m, n + 1), l, n+1)); return 0; }
以上是关于luogu2054 洗牌 同余方程的主要内容,如果未能解决你的问题,请参考以下文章
luogu_P2054 bzoj 1965 洗牌 题解 快速幂 快速乘