题解luoguP2054 BZOJ1965【[AHOI2005]洗牌】

  • 题目链接:

https://www.luogu.org/problemnew/show/P2054

https://www.lydsy.com/JudgeOnline/problem.php?id=1965

  • 思路:

首先一开始我看错题了,但是发现了一些有趣的东西:

我一开始理解的意思:第L张扑克牌的牌面大小是多少

这是题目要我们求得,然后我看成了原序列中第L张扑克牌的位置...然后兴冲冲地找到了规律,比较有趣: \(pos \equiv (L*2^m) mod (n+1)\),具体怎么证我不会,可惜数竞大佬都去集训了,现在也没办法。

然后按这个规律交了一发只有10分,才发现看错题了。我们要求的是m次洗牌后第L张扑克牌的牌面大小,然而刚刚我们已经发现了\(pos \equiv (L*2^m) \pmod {n+1}\)

于是我们设位于L位,即现在位置(pos)在L上的牌在原序列第x张,根据上面规律:\((x*2^m) \equiv L \pmod {n+1}\)

所以 $x \equiv L*{2^m}^{-1} \pmod {n+1} $,求出\(2^m\)在模\((n+1)\)意义下的逆元即可。

  • 其他:

这里不用\((n+1)\)不一定是质数,所以最好不要用费马小定理求,否则你只有40分,然后我们就只能用拓欧了

同时我发现当m满足\(2^m \equiv 1 \pmod {n+1}\)时,恰好变成最开始的序列,然而我还是不会证明

  • 代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define ll long long 
using namespace std;
ll quick_pow(int a,int b,int c)//a^b%c
{
    if(b==0)return 1;
    ll x=quick_pow(a,b>>1,c);
    x=x*x%c;
    if(b&1)x=x*a%c;
    return x; 
}
void ex_gcd(ll a, ll b, ll &x, ll &y, ll &d){
    if (!b) {d=a,x=1,y=0;}
    else{
        ex_gcd(b,a%b,y,x,d);
        y=y-x*(a/b);
    }
    return ;
}
ll inv(ll t, ll p){
    ll d,x,y;
    ex_gcd(t,p,x,y,d);
    return (x%p+p)%p;
}
int main(){
    ll n,m,l;
    cin>>n>>m>>l;
    ll p=quick_pow(2,m,n+1);
    if(p%(n+1)==1){
        printf("%lld",l);
    }
    else{
        printf("%lld\n",(l*inv(p,n+1))%(n+1));
    }
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/Rye-Catcher/p/8991133.html
今日推荐