题目:
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。
本题采用数学上的平面直角坐标系,即 x𝑥 轴向右为正方向,y𝑦 轴向上为正方向。
如果一个星星的左下方(包含正左和正下)有 k𝑘 颗星星,就说这颗星星是 k𝑘 级的。
例如,上图中星星 5 是 3 级的(1,2,4 在它左下),星星 2,4 是 1 级的。
例图中有 1 个 0 级,2个 1 级,1 个 2 级,1 个 3 级的星星。
给定星星的位置,输出各级星星的数目。
换句话说,给定 N 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
输入格式
第一行一个整数 N,表示星星的数目;
接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。
输出格式
N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N−1 级的星星的数目。
数据范围
1 ≤ N ≤ 15000,
0 ≤ x,y ≤ 32000
输入样例:
5
1 1
5 1
7 1
3 3
5 5
输出样例:
1
2
1
1
0
步骤:
1. 理解题目大意
我的理解:
一个x星的左下方(包含正左和正下)的星星才会是x星等级数的最大关键。
先想想暴力枚举怎么做,由于题目明确该题y不递减的输入特性,这导致了y在题目中毫无作用。所以暴力的第一步就是算出每一个星星前面有多少个星星的横坐标是小于当前星星的,(前缀和思想)。
又由于星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。所以我们需要在每得到一颗新的星星时,实时更新我们的前缀和数组,(单点修改及更新)
到了这一步我们就已经知道用树状数组的模型以及为什么使用树状数组来解题了
以下是代码过程:
import java.io.*;
public class Main{
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 32010;
static int[] level = new int[N]; //星级数组level[i]表示i星级的星星有多少个
static int[] Tree = new int[N]; //树状数组,Tree[x]表示x位置的星星前面有Tree[x]个的星星的横坐标是小于x的
static int n;
public static void main(String[] args) throws IOException {
String [] ss = br.readLine().split(" ");
n = Integer.parseInt(ss[0]);
for(int i = 1; i <= n; i++) {
String[] s1 = br.readLine().split(" ");
int x = Integer.parseInt(s1[0]) + 1;
//其实这里 y 并没有什么用
/*为了防止出现0的情况,给它全体横坐标加上 1 就好了。
而给每个 x 都加上 1 并不会影响结果*/
int y = Integer.parseInt(s1[1]);
add(x,1);
level[query(x)] ++;
//query(x)求1~x的和,这里其实是前缀和,
//tree[x]表示的是横坐标为x的星星前面有tree[x]个小于x
//也就对应了当前x的星级,所以在当前星级下标下+1
}
for(int i = 0; i < n; i++){
bw.write(level[i]+"\n");
}
br.close();
bw.close();
}
private static int lowbit(int x){
return x & -x;
}
private static void add(int a,int b){
for(int i = a; i < N; i += lowbit(i) ){
Tree[i] += b;
}
}
private static int query(int x){
//求1~x的和,这里其实是前缀和,
// tree[x]表示的是横坐标为x的星星前面有tree[x]个小于x
//也就对应了当前x的星级
int sum = 0;
for(int i = x; i > 0; i -= lowbit(i) ){
sum += Tree[i];
}
return sum;
}
}