题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4759
题目大意:
有2^n(n<=1000)张牌,从前至后分别为1~2^n,切牌规则,先分成两堆,第一堆依次拿第1张牌、第三张,第五章牌,,第奇数张牌,第二堆拿剩下的,然后有两种放法,可以把第一堆放到第二堆的上面也可以把第二堆放到第一堆的上面,问经过若干次切牌后,是否能够达到第A张牌为X,第B张牌为Y的状态。
解题思路:
感谢frog女神提供的思路。
考虑牌的序号(从0开始)
0 1 2 3 4 5 6 7
000 001 010 011 100 101 110 111
计算序号为i的牌,经过一次切牌后的序号。
对于序数为偶数的牌,也就是最低位为0的牌,直接循环右移一位,相当于除以二,这样就凑出了第一堆,对于序数为奇数的牌,最低位为1,循环右移一位后,相当于除二后加了2^(n-1),也就是加了前面偶数堆的长度。对于把奇数堆放到偶数堆前面的方式,只需把最高位抑或1。这样就把位置为i的牌,经过一次切牌后的位置算出来了。对于抑或可以先移位然后所有位一起抑或,但是右移操作最多只有n次,超过n次的话,就重复了,所以可以先右移n次,每右移一次(切牌)有两种方式抑或0或者1,这样就得到了一个长度为n的o、1抑或串,也就是切牌方式。枚举开始点,右移次数k,将X,Y右移K位,由于a^b=c可以推出a^c=b,所以把X抑或A与Y抑或B比较,判断是否相同(相同表示序号为X的牌和序号为Y的牌可以经过相同的切牌方式得到位置A和B)。
任何切牌方式都可以转化到构造的切牌n~2n次范围内。
代码:
import java.math.*; import java.io.*; import java.util.*; public class Main { /** * @param args */ static int n; public static BigInteger righ(BigInteger a)//循环移位 { BigInteger tmp = a.and(BigInteger.valueOf(1)); //取出最低位作为右移后的首位 return a.shiftRight(1).or(tmp.shiftLeft(n-1));//将最低为加到最高位上 } public static void main(String[] args) { // TODO Auto-generated method stub Scanner cin = new Scanner(System.in); int t=cin.nextInt(); BigInteger A,X,B,Y; for(int ca=1;ca<=t;ca++) { n=cin.nextInt(); A=cin.nextBigInteger(); X=cin.nextBigInteger(); B=cin.nextBigInteger(); Y=cin.nextBigInteger(); A=A.add(BigInteger.valueOf(-1)); //从0开始 X=X.add(BigInteger.valueOf(-1)); B=B.add(BigInteger.valueOf(-1)); Y=Y.add(BigInteger.valueOf(-1)); boolean flag=false; for(int k=1;k<=n;k++)//枚举切牌的起点 { X=righ(X); Y=righ(Y); BigInteger a=X.xor(A); BigInteger b=Y.xor(B); if(a.compareTo(b)==0) { flag=true; break; } } if(flag) System.out.println("Case "+ca+": Yes"); else System.out.println("Case "+ca+": No"); } } }