题目链接
- 题意:
给定n个球(标号1 - n)和m个盒子(标号1 - m),现在要求:当一个球x放到了i号盒子中,如果有i + 1盒子,那么x * 2球必须在里边;如果有i - 1盒子,必然有x / 2球在里边。求能1号盒子中球数量最大值
( 1≤n≤10 10000, 2≤k≤25 ) - 分析:
问题在于n太大,所以是要用java大数的。只用考虑将球放到1号盒子中,之后的球按照要求一直放到结束,关键就是保证球不重复。如果在第一个盒子中放1号球,那么2^(k - 1)号球都用过了,可以发现除了第一个,其他的球号必然是偶数。那么,第一个盒子中放奇数球肯定是可以的。之后就是要考虑偶数还能不能放。
考虑这样的问题,如果k很大,那么第一个放1号球之后,所有的2 ^ x都用过了。如果k只有1,那么只有1用过。显然,用过的球和k密切相关,那么我们可以用过1号球之后,看2 ^ k球能不能用,以此类推。其实,也就是这个想法:每个偶数一直除以2,总可以得到一个奇数。
对于每一个x,就是放1x,2x,4x....x * 2^(k -1) x * 2^k,x * 2^(k + 1) ..... x * 2 ^ (t * k - 1),也就是求x * 2 ^ (t * k - 1) <= n的最大t值就是第一个盒子中放的个数。朴素的想法是枚举x,然后求一个t,加到结果中。但是x可以到达很大(因为n很大),所以这样会超时。但是,我们可以发现一个规律,t是在指数中的,也就是说,t的范围不会很大!!大约到10000左右,所以是可以枚举的,对于一个t,我们可以找到对应的一个最小的x,和最大的x,之间的数的个数乘以t加到结果中就可以了
另外说一下,由于n比较大,java大数的常数也比较大,需要一定的优化
import java.util.*;
import java.math.*;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int T = cin.nextInt();
BigInteger zero = BigInteger.ZERO;
BigInteger one = BigInteger.ONE;
BigInteger two = BigInteger.valueOf(2);
BigInteger n; int k;
for (int i = 0; i < T; i++) {
BigInteger pre = BigInteger.valueOf(-1);
BigInteger ans = zero;
n = cin.nextBigInteger(); k = cin.nextInt();
BigInteger now = n.shiftRight(k - 1);
for (int t = 1;; t++) {
if (pre.signum() >= 0) {
if (pre.equals(now)) break;
ans = ans.add(BigInteger.valueOf(t - 1).multiply( pre.add(one).divide(two).subtract( now.add(one).divide(two) ) ));
}
pre = now;
now = now.shiftRight(k);
}
System.out.println(ans);
}
}
}