- 题目链接:
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;
}