这是第一篇博客?日志也行。。。
codeforces problem 768B
这个题做了很长时间--好几天吧,有空的时候就想怎么做。
问题描述看 http://codeforces.com/problemset/problem/768/B
看起来是个三叉树的题目。最开始我想用递归完成,遍历每一个节点,并判断节点是否在区间内,累加区间内的1。
但是2^50的数量级不可能在规定time和memory的情况下完成。
做了很多草稿,才逐渐明白以下几点,
1. 只有x mod 2才会产生0, 其余叶肯定都是1,所以只用数区间[L,R]的0的个数
2. 把每一步看做三叉树,我们只需要简单的计算(x 循环除 2,直到结果为1)就可以知道该树的最大层数 max
3. 三叉树的层数肯定不会超过50,并且每一层的“中间叶节点”都是相同的(x mod 2),用数组temp记录每一层的叶节点值
4. 第 k 层 的第 0 个叶节点左边共有 ADL = 2^(max - k + 1)个叶节点,
5. 第 k 层 的第 0 个叶节点编号为 ADL + 1 = 2^(max - k + 1) + 1,
6. 第 k 层 的第 x 个叶节点编号为多少? 同样可以算出来 location = (2 * x + 1) * ADL + 2 * x + 1 = (2 * x + 1) * (ADL + 1)
7. 到这一步,我们可以根据变量 k ∈ [1,max] 遍历出每一层每一个叶节点的位置编号,判断是否在[L,R]内,然后根据层数累加0的个数
8. 但是这样效率仍然不高,我们实际上还是遍历了整棵树的一半的叶节点。 还得根据location, L, R的关系计算 x 的范围
9. L < location < R 求出每一层的 x 的范围,然后再累加x对应的叶节点的取值.
10. 至少做到上面这些才够.事实上还可以优化.
11. 需要提前额外考虑初始值为0或1的情况。
Java源码奉上,仅供参考,
import java.util.Scanner;public class Simple {public static void main(String[] args) {// TODO Auto-generated method stubScanner sc = new Scanner(System.in);long[] data = getnumber(sc.nextLine());int[] temp = new int[51];int sum = 0;int i = 1;if(data[0] == 0 || data[0] == 1){System.out.print(data[0]);}else {for (; i < temp.length; i++) {temp[i] = (int) (data[0] % 2);data[0] = data[0] / 2;if(data[0] == 1){break;}}for (int k = 1; k <= i; k++) {long add = (long) (Math.pow(2, i-k+1) - 1);long low = (data[1] - add - 1)/(2*(add + 1));long high = (data[2] - add - 1)/(2*(add + 1));for (long x = low; x <= high; x++) {long location = (2 * x + 1) * add + 2 * x + 1;if(location >= data[1] && location <= data[2]){if(temp[k] == 0){sum++;}}}}System.out.println(data[2] - data[1] - sum + 1);}}public static long[] getnumber(String temp) {String[] t = temp.split("\\s");long[] data = new long[t.length];for(int i=0;i < t.length;i++){data[i] = Long.valueOf(t[i]);}return data;}}