2013ACM-ICPC杭州赛区全国邀请赛——X-Boxes

题目链接


  • 题意:
    给定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);
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值