1265. 数星星
题目链接
1265. 数星星
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。
如果一个星星的左下方(包含正左和正下)有 \(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\) 开始,横坐标可能为 \(0\),所以需要偏移一位
- 时间复杂度:\(O(n(log(3.2e4)))\)
代码
// Problem: 数星星
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1267/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=3.2e4+5;
int n,tr[N],res[N];
int ask(int x)
{
int res=0;
for(;x;x-=x&-x)res+=tr[x];
return res;
}
void add(int x,int y)
{
for(;x<N;x+=x&-x)tr[x]+=y;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
a++;
res[ask(a)]++;
add(a,1);
}
for(int i=0;i<n;i++)printf("%d\n",res[i]);
return 0;
}