JDOJ 1247: VIJOS-P1066 弱弱的战壕

题目传送门

Description

永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了--b。 mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒ftING...@@)。 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标x、y有一个小于它的点(mx:“我的战壕为什么这么菜”ToT)。这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队。 战壕都有一个保护范围,同它的攻击范围一样,它可以保护处在它左下方的战壕。所有处于它保护范围的战壕都叫做它的保护对象。这样,永恒就必须找到mx的战壕中保护对象最多的点,从而优先消灭它。 现在,由于永恒没有时间来计算,所以拜托你来完成这个任务: 给出这n个战壕的坐标xi、yi,要你求出保护对象个数为0,1,2……n-1的战壕的个数。

Input

第一行,一个正整数n(1< =n< =15000) 接下来n行,每行两个数xi,yi,代表第i个点的坐标 (1< =xi,yi< =32000) 注意:可能包含多重战壕的情况(即有数个点在同一坐标)

Output

输出n行,分别代表保护对象为0,1,2……n-1的战壕的个数。

Sample Input

5 1 1 5 1 7 1 3 3 5 5

Sample Output

1 2 1 1 0

题解:

首先我来概括一下这道题的题意:

在平面直角坐标系里,有n个点,这n个点会与原点一起构成一个矩形,问被其他矩形包含次数为1-n-1的点个数有多少。

我们考虑用树状数组的知识解决这个问题。

很容易看出来,这是一个二维的树状数组,代码大体长这个样子:

void add(int x,int y,int k)
{
        for(int i=x;i<=n;i+=i&-i) //行更新
        for(int j=y;j<=n;j+=j&-j) //列更新
            c[i][j]+=k; //更新元素
}
int getsum(int x,int y)
{
    int s=0;
    for(int i=x;i;i-=i&-i)
        for(int j=y;j;j-=j&-j)
            s+=c[i][j];
    return s;
}

但是,这个代码肯定会爆内存。

所以我们考虑压维。

不难看出,如果引入一个排序,把坐标重新排一遍,就可以简单地解决维度为二维的问题,因为坐标是有序的,我们枚举的时候就可以认为这个数组已经被我们压到了一个单纯的轴上。

所以当前的getsum(y)就表示已经插入到树状数组的战壕中,纵坐标小于等于当前y的战壕的个数。

注意它的定义,这个定义如果搞不明白就都是瞎扯。

所以我们就得出了大致的思路:

先结构体排序,然后再判重累加ans数组就可以。

注意!!

判重很重要!!!

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans[15001],c[32001];
struct node
{
int x,y;
}a[15001];
bool cmp(node a,node b)
{
if(a.x==b.x)
return a.y<b.y;
return a.x<b.x;
}
int getsum(int x)
{
int ret=0;
for(int i=x;i;i-=i&-i)
ret+=c[i];
return ret;
}
void fix(int x)
{
for(int i=x;i<=32001;i+=i&-i)
c[i]++;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp);
int cnt=1;
for(int i=1;i<=n;)
{
int x=getsum(a[i].y);
while(a[i].x==a[i+cnt].x && a[i].y==a[i+cnt].y)
cnt++;
ans[x]+=cnt;
fix(a[i].y);
i+=cnt;
cnt=1;
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
return 0;
}
05-07 15:17