题目连接:ZOJ 3987
原网页含有mathtype公式,复制过来会乱,所以这里就不放题了。
题意:给2个大数n和m,要求把n分成m个数相加,并且取或运算后最小。
题目分析:
首先考虑将n平分m份,如果恰好可以平分即n%m==0,则答案就是n/m。
之后考虑无法平分的情况,n/m+1会产生二进制的进位,如n=15,m=4的情况,n/m=3,n/m+1=4,产生了进位。考虑n/m+1用二进制表示,取其lowbit,比如n=32,m=6,
则n/m+1=6=110,其lowbit就是把最右边的1变为0,lowbit(6)=4;此时这m个数是一定会加上4的,剩下的再重复上述步骤,知道剩下的数小于m。如果n/m+1是2的次方数,lowbit数不变,此时这个数也是一定要取的,不过只能尽量多的取,如上述n=15,m=4的情况,前三个每个取4,后一个不取。
最后考虑n=32,m=6的情况:第一步n/m+1=6,取4,剩余32-6*4=8,8/6+1=2,则前3个数+2,后面3个数不变,剩下0,结束。
lowbit就是x&(-x)。
由于这里需要大数取模,相减,以及与运算,所以在没有详尽的大数模板情况下java是个不错的选择。
import java.io.*;
import java.util.*;
import java.math.*;
public class Main {
public static void main(String[] args)
{
int T;
BigInteger m,n,div,left,l;
BigInteger []t=new BigInteger[1111];
Scanner cin=new Scanner(System.in);
T=cin.nextInt();
for(int kase=0;kase<T;kase++)
{
n=cin.nextBigInteger();
m=cin.nextBigInteger();
div=n.divide(m);
if(n.equals(m.multiply(div)))
{
System.out.println(div);
continue;
}
BigInteger ans=BigInteger.valueOf(0);
left=n;
div=div.add(BigInteger.valueOf(1));
while(left.compareTo(BigInteger.ZERO)==1)
{
BigInteger bit=lowbit(div);
if(bit.equals(div))
{
ans=ans.add(div);
BigInteger buf=left.divide(div);
buf=buf.multiply(div);
left=left.subtract(buf);
div=left.divide(m);
div=div.add(BigInteger.valueOf(1));
}
else
{
BigInteger buf=div.subtract(bit);
ans=ans.add(buf);
buf=buf.multiply(m);
left=left.subtract(buf);
div=left.divide(m);
div=div.add(BigInteger.valueOf(1));
}
}
System.out.println(ans);
}
}
private static BigInteger lowbit(BigInteger div) {
return div.and(div.multiply(BigInteger.valueOf(-1)));
}
}