1. 程式人生 > 其它 >1265. 數星星

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;
}