Stars
Time Limit: 1000MS |
Memory Limit: 65536K |
Total Submissions: 21050 |
Accepted: 9172 |
Description
Astronomers often examine starmaps where stars are represented by points on a plane and each star hasCartesian coordinates. Let the level of a star be an amount of the stars thatare not higher and not to the right of the given star. Astronomers want to knowthe distribution of the levels of the stars.
For example, look at the map shown on the figure above. Level of the starnumber 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 areonly one star of the level 0, two stars of the level 1, one star of the level2, and one star of the level 3.
You are to write a program that will count the amounts of the stars of eachlevel on a given map.
Input
The first line of the inputfile contains a number of stars N (1<=N<=15000). The following N linesdescribe coordinates of stars (two integers X and Y per line separated by aspace, 0<=X,Y<=32000). There can be only one star at one point of theplane. Stars are listed in ascending order of Y coordinate. Stars with equal Y coordinatesare listed in ascending order of X coordinate.
Output
The output should contain Nlines, one number per line. The first line contains amount of stars of thelevel 0, the second does amount of stars of the level 1 and so on, the lastline 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 inputdata,use scanf() instead of cin to read data to avoid time limit exceed.
Source
Ural CollegiateProgramming Contest 1999
【题目大意】
在一个坐标系内,给若干个点,每个点给定x、y坐标,定义点p的level为横纵坐标均不超过点p的点的个数,输出每个level包含的点的数量。
【我的算法】
不应用树状数组的时候,用两个数组分别储存输入的横坐标和纵坐标,然后用一个结构体的数组存储输入的结点。然后依次计算每一个结点的level值。
代码如下:
#include <iostream>
using namespace std;
const int MAX= 32000;
int ArrayX[MAX]={0};
int ArrayY[MAX]={0};
struct Node
{
int x;
int y;
};
int Sum(int end, int* array)
{
int sum = 0;
while(end >= 0)
{
sum+= array[end];
end--;
}
return sum;
}
int main()
{
int N;
cin>>N;
int *count = new int[N-1];//结果计数
struct Node *node = new struct Node[N];//存储输入的坐标点
int x,y,i = 0, n = N;
while (n-->0)
{
cin>>x>>y;
node[i].x = x;
node[i++].y = y;
ArrayX[x]++;
ArrayY[y]++;
}
for (int co= 0; co < N; co++)
{
count[co] = 0;
}
for (int j = 0; j < N; j++)
{
int littleXNum,littleYNum, littleNum;
littleXNum= Sum(node[j].x-1, ArrayX);//计算比node结点横坐标小的点的个数
littleYNum= Sum(node[j].y-1, ArrayY);//计算比node结点纵坐标小的点的个数
littleNum= littleXNum < littleYNum? littleXNum : littleYNum;//计算比node结点的横坐标和纵坐标都小的点的个数
if(ArrayX[node[j].x] == 1&& ArrayY[node[j].y] == 1)//如果,此点的横坐标和纵坐标上没有其他点了则进行下一个结点的判断
{
count[littleNum]++;
continue;
}
for(int k = 0; k < j; k++)//计算此点的横坐标和纵坐标上其他符合条件的点
{
if (node[k].x == node[j].x && node[k].y <= node[j].y) //计算此点的纵坐标方向上其他符合条件的点
littleNum++;
if (node[k].x <= node[j].x && node[k].y == node[j].y) //计算此点的横坐标方向上其他符合条件的点
littleNum++;
}
count[littleNum]++;
}
for (int c = 0; c < N; c++)
{
cout<<count[c]<<endl;
}
system("pause");
return0;
}
【错误原因分析】
原因:结果虽然正确,但是提交后TLE了。时间复杂度太高。没有应用树状数组。
心得:好的数据结构来带来高效的算法。
【正确算法】
这道题目和计数有关,是典型的树状数组应用的题目。题目给的数据按照y排序,相同的y按照x排序,这就给解题带来了很大的方便,我们可以按照坐标系中由下至上(y由小到大)、由左至右(x由小到大)的顺序来处理数据,这样我们只利用x就可以进行计数了,因为当我们处理到任意点p的时候,所有的y小于p的点我们已经计数过了,只需要统计所有x不超过p的点的数量就可以了。而这一步利用树状数组强大的计数功能实现。
由于x的范围是[0,32000],可以申请一个长度为32001的计数数组sum,sum[i]表示x不大于i的点的数目,每次统计完i的数目后,要把sum[i]计数增加1。
代码如下:
#include<iostream>
using namespace std;
#define N 32001
int sum[N+1];
//算这个^k有一个快捷的办法,定义一个函数如下即可
//利用机器补码的特点,这个函数可以改得更方便
int lowbit(int k)
{
return k&(-k);
}
//如果要把a[i]增加v,可以通过调用如下函数实现
void add(int i,int v)
{
while (i<=N)
{
sum[i]+=v;
i+=lowbit(i);
}
}
//如果要统计a[1]到a[i]之间的和,可以通过调用如下函数实现
int getSum(int i)
{
int s=0;
while (i>0)
{
s+=sum[i];
i-=lowbit(i);
}
return s;
}
int main()
{
int n,i,x,y;
cin>>n;
int *result = new int[n];
//初始化数组
for(i=0;i<n;i++)
{
result[i]=0;
}
for(i=1;i<=N;i++)
{
sum[i]=0;
}
for(i=0;i<n;i++)
{
cin>>x>>y;
result[getSum(x+1)]++;//统计
add((x+1),1); //更新sum[i]的值
}
for(i=0;i<n;i++)
{
cout<<result[i]<<endl;
}
return1;
}