计蒜客-奇怪的报数游戏
原创
©著作权归作者所有:来自51CTO博客作者tizzi的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、内容
二、思路
- 从后往前推,最后一个人的编号一定是排在它前面的人个数+ 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;
}
}