题意:给出n个点的坐标(n<=15000),坐标范围为[1,32000],最后让你输出ans[i],表示有ans[i]个点左下方有i个点。
一开始直接写的二维树状数组,记录一个矩阵,如果(x,y)有点就是1,没有点就是0,那么我们可以轻易求出(1,1)到(x,y)共有多少点,不过这样就爆了10倍内存。(1K == 256 int型整数)
然而后来才注意到:题目输入是按照y坐标从小到大输入的。(英语水平很重要TAT)
y坐标按照从小到大输入,所以可以只用一维的树状数组,只需要记录横坐标上1到x有多少个点即可。需要离散化,一开始写的没有离散化的T掉了。
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define lowbit(x) x&-x
using namespace std;
int n, m, ans[15005], x[15005], y, f[32005];
struct star{
int x, num;
}t[15005];
bool cmp(star a, star b){
return a.x < b.x;
}
void add(int x){
while(x <= n) f[x]++, x += lowbit(x);
}
int sum(int x){
int ans = 0;
while(x) ans += f[x], x -= lowbit(x);
return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d %d", &t[i].x, &y);
t[i].num = i;
}
sort(t+1, t+n+1, cmp);
int cnt = 1;
x[t[1].num] = 1;
for(int i = 2; i <= n; i++){
if(t[i].x == t[i-1].x) x[t[i].num] = cnt;
else x[t[i].num] = ++cnt;
}
for(int i = 1; i <= n; i++){ //☆☆
add(x[i]);
ans[sum(x[i])-1]++;
}
for(int i = 0; i < n; i++)
printf("%d\n", ans[i]);
}
有细节需要注意的是,空间用一维树状数组的区别就是代码里标五角星的那个for循环,如果是二维的话应该是这么写的:
for(int i = 1; i <= n; i++)
add(t[i].x, t[i].y);
for(int i = 1; i <= n; i++){
int x = t[i].x, y = t[i].y, k;
k = sum(x-1, y) + sum(x, y-1) - sum(x-1, y-1);
ans[k]++;
}
这个需要实际理解一下左下方的含义,(x,y)的左下方的意思是(1,1)到(x,y)的矩形内的范围(除了自己),所以在一维的计算中是ans[sum(x[i])-1]++, 二维就是sum(x-1, y) + sum(x, y-1) - sum(x-1, y-1) (当然二维也可以是sum(x,y)-1),二维的两种写法都很好理解,一维的就只有那一种写法(sum(x[i]-1)是不对的,因为有可能会有x坐标相同的,y坐标比当前点小的点,而它们在一维的树状数组中都被记录成同一个x坐标),需要好好体会一下。