一、内容

计蒜客-奇怪的报数游戏_复杂度
计蒜客-奇怪的报数游戏_java_02

二、思路

  • 从后往前推,最后一个人的编号一定是排在它前面的人个数+ 1,依次往前推。当我们往前推的时候,后面出现的数就不会再占排名了,所以我们要用树状数组维护已经出现了的编号。
  • 用树状数组维护1-1 1-2 1-3····1-n区间里面的值表示已经出现了的编号的次数,初始都为0
  • 然后用二分去搜索符合的区间,这样复杂度就是0(nlg2n)
     for (int i = n - 1; i >= 0; i--) {
           	//二分进行扫描
           	int l = 1, r = n;
           	int save = 0;
           	while (l <= r) {
           		int mid = (l + r) >> 1;  
           		int tem = getNum(mid); //mid 代表的就是编号
           		if (tem + 1 + rec[i] <= mid) {//若区间值 + 1 + 排名 <= 编号 那么说明这个值可能是答案 用save记录一下
           			r = mid - 1;
           			save = mid;
           		} else {
           			l = mid + 1;
           		}
           	}
           	//当退出循环时,save就是答案
           	num[i] = save;
           	//将编号包含的区间都加1
           	update(save, 1);
           }
    

三、代码

import java.io.PrintWriter;
import java.util.Scanner;

public class G_奇怪的报数游戏 {
	static int N = 500000;
	static int[] c = new int[N + 5];
	static int[] rec = new int[N + 5];
	static int[] num = new int[N + 5];
	static int n, top;
	static PrintWriter out = new PrintWriter(System.out);
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		for (int i = 1; i < n; i++) {
			rec[i] = sc.nextInt();
		}
		//最后一个数肯定是比它小的数+1
	    for (int i = n - 1; i >= 0; i--) {
	    	//二分进行扫描
	    	int l = 1, r = n;
	    	int save = 0;
	    	while (l <= r) {
	    		int mid = (l + r) >> 1;
	    		int tem = getNum(mid);
	    		if (tem + 1 + rec[i] <= mid) {
	    			r = mid - 1;
	    			save = mid;
	    		} else {
	    			l = mid + 1;
	    		}
	    	}
	    	num[i] = save;
	    	update(save, 1);
	    }
	    for (int i = 0; i < n; i++) {
	    	out.println(num[i]);
	    }
		out.close();
	}
	static void update(int x, int v) {
		for (int i = x; i <= n; i += i & (-i)) {
			c[i] += v;
		}
	}
	static int getNum(int x) {
		int ans = 0;
		for (int i = x; i > 0; i -= i & (-i)) {
			ans += c[i];
		}
		return ans;
	}
}