题目链接:
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");
}
}
}