【BZOJ1965】[AHOI2005] SHUFFLE 洗牌(数学题)

这是一道关于洗牌的数学题,当扑克牌被平均分为上下两叠并交替取牌时,探讨经过m次洗牌后第l张扑克牌的编号。通过数学分析,可以得出位置变化规律,利用模运算和乘法逆元求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点此看题面

大致题意: 有一叠扑克牌编号为 1 ∼ n 1\sim n 1n n n n为偶数),每次洗牌将扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。问 m m m次洗牌后第 l l l张扑克牌的编号。


数学题

n , m n,m n,m这么大,比较显然是一道数学题。

设当前位置为 x x x,则不难发现,每次洗牌之后,下一次的位置为: 2 x 2x 2x% ( n + 1 ) (n+1) (n+1)

以此类推,经过 m m m次洗牌,这张扑克牌的位置应为 2 m x 2^mx 2mx% ( n + 1 ) (n+1) (n+1)

既然这样,题目要我们求的就出一个 x x x满足 2 m x ≡ l ( m o d   n + 1 ) 2^mx\equiv l(mod\text{ }n+1) 2mxl(mod n+1)

根据等式的性质,原式可化为 x ≡ l ⋅ ( 2 m ) − 1 ( m o d   n + 1 ) x\equiv l·(2^m)^{-1}(mod\text{ }n+1) xl(2m)1(mod n+1)

因此,我们只需求出 2 m 2^m 2m在模 n + 1 n+1 n+1意义下的乘法逆元,再乘以 l l l,就可以求出 x x x了。

L i n k Link Link

乘法逆元 详见博客 浅谈乘法逆元的三种解法


代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (putchar(ch))
int OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,OutputStack[Fsize];
using namespace std;
LL n,m,l;
inline void read(LL &x)
{
    x=0;static char ch;
    while(!isdigit(ch=tc()));
    while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
inline void write(LL x)
{
    if(!x) return (void)pc('0');
    while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
    while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
}
inline LL quick_pow(LL x,LL y,LL MOD)//快速幂
{
    register LL res;
    for(res=1;y;(x*=x)%=MOD,y>>=1) if(y&1) (res*=x)%=MOD;
    return res;
}
inline void exgcd(LL x,LL y,LL &s1,LL &s2)//exgcd求逆元
{
    if(!y) return (void)(s1=1,s2=0);
    exgcd(y,x%y,s2,s1),s2-=x/y*s1;
}
inline LL Inv(LL x,LL y)//求逆元
{
    register LL s1,s2;
    exgcd(x,y,s1,s2);
    return (s1%y+y)%y;
}
int main()
{
    return read(n),read(m),read(l),write(Inv(quick_pow(2,m,n+1),n+1)*l%(n+1)),0;//求出2^m在模n+1意义下的逆元乘以l模n+1后的值
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值