[bzoj1965] 洗牌

题意:给你一副\(10^{10}\)级别的扑克牌,一开始按1-n排列,然后每次重排,将原排列分成两份,取第二份的第一个为新排列的第一个,取第一份的第一个为新排列的第二个,依次类推,问你重排m次后,第l位上的牌是多少。

题解:

数论

发现规律:重排一次后,\(x\)的位置变为\(2*x\pmod{n+1}\)

然后设x为l位上的数字,有\(x*2^m{\equiv}l\pmod{n+1}\)

\(2\)\(n+1\)下的逆元是\(n/2+1\),则有\(x\equiv(n/2+1)^m*l\pmod{n+1}\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;

ll gi() {
  ll x=0,o=1; char ch=getchar();
  while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
  return o*x;
}

ll mul(ll x, ll y, ll p) {
  ll ret=0;
  while(y) {
    if(y&1) (ret+=x)%=p;
    (x+=x)%=p,y>>=1;
  }
  return ret;
}

ll qpow(ll x, ll y, ll p) {
  ll ret=1;
  while(y) {
    if(y&1) ret=mul(ret,x,p)%p;
    x=mul(x,x,p)%p,y>>=1;
  }
  return ret;
}

int main() {
  ll n=gi(),m=gi(),l=gi();
  ll inv=qpow(n/2+1,m,n+1);
  printf("%lld\n", mul(inv,l,n+1));
  return 0;
}

posted @ 2017-10-07 23:07  HLX_Y  阅读(136)  评论(0编辑  收藏  举报