题意:
一个数n,分成m个数a1...am (n,m很大),使a1 OR a2 OR ... OR an的值最小,输出这个最小值。
思路:
- 首先有一个OR的性质:多个数OR,如果一个数二进制某一位是1, 最后答案这一位一定是1。那么要让结果最小,肯定每个数二进制最高的1位要小。
- 第二:答案是个OR的最小值,那么说明答案中的最高位的1不能再减小了,(如果可以降位那就不是最小的答案了),那么这一点感性认识上可能存在方法去构造最小的答案。
- 怎么理解呢?至少能通过方法找到最高位1的位置。那么这个题贪心的思路就有了。如果处理第i位时,m个数0 ~ i-1位全是1也不能凑出剩下的n,那第i位必然是1.由于第i位确定是1,那一定要尽可能多的让第i位是1(反正最后答案这一位都已经是1了,多一些位是1结果都一样).这样我们就能确定每一位是否是1.那就构造出答案了。
- 这题值得多想想。
//c++写的话大致思路
int n;
int len = //n二进制位数
for(int i = len - 1; i >= 0 && n > 0; -- i){
int tmp = 1 << i;
temp = (tmp - 1) * m;
if(temp < n){
n = n - tmp * min(m, n / tmp);
ans = ans | tmp;
}
}
cout<< ans <<endl;
import java.io.*;
import java.math.*;
import java.util.Scanner;
public class Main {
static BigInteger n, m;
public static void solve() {
int len = 0;
BigInteger zero = BigInteger.ZERO;
BigInteger res = zero;
len = n.bitLength();
for(int i = len - 1; i >= 0 && n.compareTo(zero) > 0; i --) {
BigInteger tmp = BigInteger.ONE.shiftLeft(i);
BigInteger temp = (tmp.subtract(BigInteger.ONE)).multiply(m);
if(temp.compareTo(n) < 0) {
n = n.subtract(tmp.multiply(m.min(n.divide(tmp))));
res = res.or(tmp);
}
}
System.out.println(res);
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int T; T = cin.nextInt();
while(T -- > 0) {
n = cin.nextBigInteger();
m = cin.nextBigInteger();
solve();
}
}
}