BZOJ 1965 [Ahoi2005]SHUFFLE 洗牌:快速幂 + 逆元
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1965
题意:
对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。
如果对一叠6张的扑克牌1 2 3 4 5 6,进行一次洗牌的过程如下图所示:
游戏是这样的,如果给定长度为N的一叠扑克牌,并且牌面大小从1开始连续增加到N(不考虑花色),对这样的一叠扑克牌,进行M次洗牌。最先说出经过洗牌后的扑克牌序列中第L张扑克牌的牌面大小是多少的科学家得胜。小联想赢取游戏的胜利,你能帮助他吗?
题解:
对于一张牌,若当前位置为x,则一次洗牌后,位置变为(x*2)%(n+1)。
所以m次洗牌后,位置为L = (x * 2^m) % (n+1)。
所以原来的位置x = L * (2^m关于n+1的逆元) % (n+1)
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 5 using namespace std; 6 7 long long n,m,l; 8 9 void exgcd(long long a,long long b,long long &x,long long &y) 10 { 11 if(b==0) 12 { 13 x=1,y=0; 14 return; 15 } 16 exgcd(b,a%b,y,x); 17 y-=(a/b)*x; 18 } 19 20 long long inv(int i,int p) 21 { 22 long long x,y; 23 exgcd(i,p,x,y); 24 return (x%p+p)%p; 25 } 26 27 long long quick_pow(long long x,long long k,long long p) 28 { 29 long long ans=1; 30 while(k>0) 31 { 32 if(k&1) ans=ans*x%p; 33 x=x*x%p,k>>=1; 34 } 35 return ans; 36 } 37 38 int main() 39 { 40 cin>>n>>m>>l; 41 cout<<(l*inv(quick_pow(2,m,n+1),n+1))%(n+1)<<endl; 42 }