zoukankan      html  css  js  c++  java
  • BZOJ1965 [Ahoi2005]SHUFFLE 洗牌 快速幂

    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ1965


    题意概括

      对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两叠,取下面一叠的第一张作为新的一叠的第一张,然后取上面一叠的第一张作为新的一叠的第二张,再取下面一叠的第二张作为新的一叠的第三张……如此交替直到所有的牌取完。

      经过一次洗牌,序列1 2 3 4 5 6变为4 1 5 2 6 3。当然,再对得到的序列进行一次洗牌,又会变为2 4 6 1 3 5。 游戏是这样的,如果给定长度为N的一叠扑克牌,并且牌面大小从1开始连续增加到N(不考虑花色),对这样的一叠扑克牌,进行M次洗牌。

      扑克牌序列中第L张扑克牌的牌面大小是多少?


    题解

      我们发现,一次操作其实就是把第i个位置的牌放到第(2*i) mod (n+1)个位置。

      于是我们可以列出方程:

      设答案为x,

      x • 2m Ξ L (mod (n + 1))

      mod (n+1)条件下,2 的逆元是 n/2+1

      故可以移项,得:

      x = (n / 2 + 1)m • L mod (n + 1)

      于是快速幂跑一跑就可以了。

      但是会有中间乘法溢出的情况。

      一位一位乘就可以了。


    代码

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #include <cstdlib>
    using namespace std;
    typedef long long LL;
    LL n,m,L,mod;
    LL times(LL a,LL b){
    	LL ans=0;
    	for (int i=40;i>=0;i--){
    		ans=(ans<<1)%mod;
    		if ((b>>i)&1)
    			ans=(ans+a)%mod;
    	}
    	return ans;
    }
    LL Pow(LL a,LL b){
    	LL ans=1,now=a;
    	while (b){
    		if (b&1)
    			ans=times(ans,now);
    		now=times(now,now);
    		b>>=1;
    	}
    	return ans;
    }
    int main(){
    	scanf("%lld%lld%lld",&n,&m,&L);
    	mod=n+1;
    	printf("%lld",times(Pow(n/2+1,m),L));
    	return 0;
    }
    

      

  • 相关阅读:
    后期生成事件命令copy /y
    SevenZipShaper压缩类
    vs2017
    WCF路由服务
    微服务--
    各种流程图的绘画网路工具 processon
    ROC 准确率,召回率 F-measure理解(转载)
    Unix OpenCV安装
    转载:tar 解压缩命令~
    cppreference经验总结
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1965.html
Copyright © 2011-2022 走看看