1265. 数星星
星星的坐标按照\(y\)坐标升序给出,当\(y\)坐标相同时,按照\(x\)坐标升序给出,那么可以得到给出的顺序时是按照\(y\)坐标分层,从左向右给出的,即先给出\(y = 1\)一层,再给出\(y = 2\)一层的所有星星...
那么有结论:在给出某一个星星的坐标\((x_i, y_i)\)时,它的\(y\)坐标一定是最大的,所以位于\((x_i, y_i)\)左下(包括左边和下面)的所有点就是之前的所有\(x\)坐标小于等于\(x_i\)的所有星星。
需要一边求前缀和,一边更新,若暴力则\(O(n^2)\)超时
前缀和+单点修改 => 树状数组\(O(nlogn)\)
本题其实是用树状数组维护了另一个数组,这个数组存的是每一种x下标下星星的个数
#include<iostream>
using namespace std;
const int N = 32001, M = 15010;
int tr[N], level[M];
int n;
int lowbit(int x){
return x & -x;
}
int query(int x){ // 返回1~x前缀和
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void add(int x, int v){
for(int i = x; i <= N; i += lowbit(i)) tr[i] += v;
}
int main(){
cin >> n;
for(int i = 0; i < n; i ++){
int x, y;
cin >> x >> y;
x ++; // 因为树状数组的下标必须从1开始
level[query(x)] ++;
add(x, 1);
}
for(int i = 0; i < n; i ++) cout << level[i] << endl;
return 0;
}
线段树硬(↘)干(↘)
#include<iostream>
using namespace std;
const int N = 32010, M = 15010;
#define PII pair<int, int>
struct Node{
int l, r;
int sum;
}tr[N * 4];
int n;
int level[N];
int xs[N];
void pushup(int u){
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r){
if(l == r) tr[u] = {l, r, xs[l]};
else{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x, int v){
if(tr[u].l == tr[u].r) tr[u].sum += v;
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
int query(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if(l <= mid) sum = query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int main(){
cin >> n;
build(1, 0, N);
for(int i = 0; i < n; i ++){
int x, y;
cin >> x >> y;
level[query(1, 0, x)] ++;
modify(1, x, 1);
}
for(int i = 0; i < n; i ++) cout << level[i] << endl;
return 0;
}