题解luoguP2054 BZOJ1965【[AHOI2005]洗牌】
-
题目链接:
-
思路:
首先一开始我看错题了,但是发现了一些有趣的东西:
我一开始理解的意思:第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
include
include
include
include
include
define ll long long
using namespace std;
ll quick_pow(int a,int b,int c)//a^b%c
{
if(b0)return 1;
ll x=quick_pow(a,b>>1,c);
x=xx%c;
if(b&1)x=xa%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;
}