星星的坐标按照(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;
}