Stars
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 40026 | Accepted: 17373 |
Description
Astronomers often examine star maps where stars are represented by points on a plane and each star has Cartesian coordinates. Let the level of a star be an amount of the stars that are not higher and not to the right of the given star. Astronomers want to know the distribution of the levels of the stars.
For example, look at the map shown on the figure above. Level of the star number 5 is equal to 3 (it's formed by three stars with a numbers 1, 2 and 4). And the levels of the stars numbered by 2 and 4 are 1. At this map there are only one star of the level 0, two stars of the level 1, one star of the level 2, and one star of the level 3.
You are to write a program that will count the amounts of the stars of each level on a given map.

For example, look at the map shown on the figure above. Level of the star number 5 is equal to 3 (it's formed by three stars with a numbers 1, 2 and 4). And the levels of the stars numbered by 2 and 4 are 1. At this map there are only one star of the level 0, two stars of the level 1, one star of the level 2, and one star of the level 3.
You are to write a program that will count the amounts of the stars of each level on a given map.
Input
The first line of the input file contains a number of stars N (1<=N<=15000). The following N lines describe coordinates of stars (two integers X and Y per line separated by a space, 0<=X,Y<=32000). There can be only one star at one point of the plane. Stars are listed in ascending order of Y coordinate. Stars with equal Y coordinates are listed in ascending order of X coordinate.
Output
The output should contain N lines, one number per line. The first line contains amount of stars of the level 0, the second does amount of stars of the level 1 and so on, the last line contains amount of stars of the level N-1.
Sample Input
5 1 1 5 1 7 1 3 3 5 5
Sample Output
1 2 1 1 0
Hint
This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed.
題目大意:
給你n個點代表n個星星,以該點為起點向x,y軸作垂線,會得到一個矩形,這個矩形里面所包含的星星數(不包括它本身)稱為該星星的等級
,要求輸出在0-n-1等級各有多少個星星。注意給出的點的順序很有講究,是按照y坐標升序給出,如果y坐標相同,則按照x坐標升序給出,題目
這樣說就相當於減少了這個題目的難度,如果題目沒有給出這樣的提示,我們也需要這樣進行處理。
線段樹菜鳥總結:因為才剛剛接觸線段樹,我是再看ppt學線段樹的過程中看到了這一道題目,所以才過來做了一下,如果讓我自己去做,十有八九是
做不出來的,畢竟線段樹才做了兩道題,借這一道題也順便說一下我的拙見吧。剛開始學的線段樹是包含三個函數,build函數,change(update)函數
find函數,進行基本的線段樹建立,查詢,更新,這個模板也讓我解決了一小部分題目,如hdu1166(敵兵布陣),hdu1754(I hate it),這兩道題
目對線段樹使用的提示太明顯了,線段容易尋找,如軍營編號,學生編號,而且輸出也是圍繞對某個葉子節點的更改或者是對某一個區間的查詢,同時
子節點與父節點的關系也相當明顯,如父節點等於左右子節點之和或左右子節點中最大值,實踐才是進步的階梯,盡管有模板,但在做這兩道題的過程中
還是出現了一些問題,自己對函數參數的理解更加深刻了一些,同時也知道tree數組應該開多大(TLE的我好痛),也有幾點優化小技巧,比如:
tips 1:在線建樹--即不開線段樹組 ,邊輸入邊建樹,這樣可以節省一定的內存.
tips 2:線段樹進行的各種功能都是圍繞二分思想,在*2,/2的時候可以用>>1,<<1來代替,可以縮短時間。
做完了這兩道題,內心還是有一丟丟得意,以為已經基本掌握了線段樹,然而我又做了這一道stars,才知道自己才是圖樣圖森破,orz,線段樹博大精深,那
兩道水題只是敲門磚。。。線段樹靈活的多,離掌握線段樹我還有着很遠的距離。
本道題思路分析:首先要發現這道題的特點,數據非常大,暴力做必死,因為坐標是按照y升序給出,這就保證了先前的星星的所構成的矩形中部可能包含
后給出的星星,這樣就可以邊建樹邊查找,更新level數組,這與我之前做的那兩道題就有着區別了,另外兩道題是先將樹建好,然后再進行查找更新操作,
然這道題絕對不能這么做,因為線段樹是一維的,然而坐標本來是兩維的,這道題之所以能夠使用線段樹,是因為y坐標按升序給出,這樣也就不用考慮
y坐標了,如果是先建樹后查找就會出現問題,雖然x坐標大,但是y坐標未必是最大的,這樣就會出錯,因此必須要邊建樹邊查找,這樣就可以保證后面
給出的點不會影響查找結果的正確性。好神奇。。orz,同時區別於那兩道題,那兩道題不管是學生序號還是軍營序號都是明確從1-n,所以可以直接數組
儲存,進行建樹,但是這道題x坐標未必是連續的,這就要求我們定向建樹,所以在build函數里面加了一個參數x,來進行定向建樹。。orz
這都是新姿勢。。。orz 繼續努力
代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=32000+100;
int x[maxn],tree[4*maxn];
int level[15000+50];//儲存不同level的星星個數
void build(int p,int l,int r,int x)//x來界定建哪一個位置的樹,該函數既是建樹也是對樹的更新
{
if(l==r) {tree[p]++;return;}//節點的含義,該位置有一個星星(0或1)
int mid=(l+r)/2;
if(mid>=x) build(2*p,l,mid,x);
else build(2*p+1,mid+1,r,x);
tree[p]=tree[2*p]+tree[2*p+1];//這個區間內星星的個數
}
int find(int p,int l,int r,int x,int y)
{
if(l<=x&&r<=y) {return tree[p];}
int mid=(l+r)/2;
if(y<=mid) return find(2*p,l,mid,x,y);
if(x>mid) return find(2*p+1,mid+1,r,x,y);
else return find(2*p,l,mid,x,mid)+find(2*p+1,mid+1,r,mid+1,y);
}
int main()
{
int n,y,maxn=-1;
scanf("%d",&n);
memset(tree,0,sizeof(tree));
memset(level,0,sizeof(level));
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y);
if(x[i]>maxn) maxn=x[i];//用maxn來作為線段樹邊界
}
for(int i=1;i<=n;i++)
{
build(1,0,maxn,x[i]);
level[find(1,0,maxn,0,x[i])-1]++;
}
for(int i=0;i<n;i++)
printf("%d\n",level[i]);
return 0;
}
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=32000+100;
int x[maxn],tree[4*maxn];
int level[15000+50];//儲存不同level的星星個數
void build(int p,int l,int r,int x)//x來界定建哪一個位置的樹,該函數既是建樹也是對樹的更新
{
if(l==r) {tree[p]++;return;}//節點的含義,該位置有一個星星(0或1)
int mid=(l+r)/2;
if(mid>=x) build(2*p,l,mid,x);
else build(2*p+1,mid+1,r,x);
tree[p]=tree[2*p]+tree[2*p+1];//這個區間內星星的個數
}
int find(int p,int l,int r,int x,int y)
{
if(l<=x&&r<=y) {return tree[p];}
int mid=(l+r)/2;
if(y<=mid) return find(2*p,l,mid,x,y);
if(x>mid) return find(2*p+1,mid+1,r,x,y);
else return find(2*p,l,mid,x,mid)+find(2*p+1,mid+1,r,mid+1,y);
}
int main()
{
int n,y,maxn=-1;
scanf("%d",&n);
memset(tree,0,sizeof(tree));
memset(level,0,sizeof(level));
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y);
if(x[i]>maxn) maxn=x[i];//用maxn來作為線段樹邊界
}
for(int i=1;i<=n;i++)
{
build(1,0,maxn,x[i]);
level[find(1,0,maxn,0,x[i])-1]++;
}
for(int i=0;i<n;i++)
printf("%d\n",level[i]);
return 0;
}
代碼: