hdu 1541 Stars cdq分治

Stars

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 15701    Accepted Submission(s): 5871


 

Problem 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.

 

 

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

 

 

Source

Ural Collegiate Programming Contest 1999

 

 

Recommend

LL   |   We have carefully selected several similar problems for you:  1166 1394 3450 1542 1255 

 题目大意:给出了一些星星的坐标,求对于每一个星星,它的左下角有多少个星星,题目保证每个点只有一个星星。

解题思路:本题原是树状数组的题目,因为y坐标已经有序,所以对于每个星星的x坐标查询小于它的x有多少个即可。

由此可见,对x排序是一个降维的方法。

cdq分治便是处理一些偏序问题的有效方法,对于此题转化为2维偏序问题,即为

对于每一个坐标,求x,y都小于它的数量。

所以可以用cdq分治来解决。

分治的思想就是把大的问题划分成小的问题后再合并。

cdq分治的大致过程就是:

1.将大区间分成两个小的区间。

2.递归解决两个小区间。

3.处理左区间对右区间的影响。

归并排序和cdq分治的思想差不多。

举个例子:如果我们先对所有的x坐标排序的话,我们就不用考虑x坐标的影响了(排序是一种降维的方法)。

对于y,我们可以不断的插入到树状数组中统计答案,也可以模仿归并排序的过程统计答案。

但是为什么这样是对的呢。

对于原来的区间,我们将它分成了两部分,对于每个坐标,对他的答案有贡献的来自于两个部分,

即区间内部的和前一半区间的。

我们在分别统计完内部的贡献之后,单独处理一下左半区间对右半区间的影响即可。

cdq是用来降低维度的,所以也可以不排序,一种用cdq嵌套下去。

多一层就多一个log。

但是四维以上的貌似不用cdq了。

代码实现就是下边:写的太麻烦了 。。。。。

ps:在cdq_2中要另外再开一个数组,因为a数组正在对x归并,而cdq2里边是对y归并,如果用a数组的话会把顺序打乱。

#include<bits/stdc++.h>
#define LL long long
#define sca(x) scanf("%d",&x)
using namespace std;
const int N = 15000+5;

typedef pair<int,int>pii;
struct node
{
    int x,y,z;
    int flag,id;
    friend bool operator <(node a,node b)
    {
        if(a.x!=b.x) return a.x<b.x;
        if(a.y!=b.y) return a.y<b.y;
        return a.z<b.z;
    }
    pii ans;
}a[N],b[N],c[N];

int ou[N];
void cdq_2(int l,int r)
{
    if(l>=r) return ;
    int m=(l+r)>>1;
    cdq_2(l,m);cdq_2(m+1,r);
    int tot=0,i,j;
    int cnt=l-1;
    for( i=l,j=m+1;j<=r;j++)
    {
        while(b[i].y<=b[j].y &&i<=m)
        {
            if(b[i].flag==1)tot++;
            c[++cnt]=b[i];
            i++;
        }
        c[++cnt]=b[j];
        if(b[j].flag==2)
        {
            ou[b[j].id]+=tot;
        }
    }
    while(i<=m)
    {
        if(b[i].flag==1)tot++;
        c[++cnt]=b[i++];
    }
    while(j<=r)
    {
        c[++cnt]=b[j++];
        if(b[j-1].flag==2)ou[b[j-1].id]+=tot;
    }

    for(int i=l;i<=r;i++)b[i]=c[i];
}

void cdq_1(int l,int r)
{
    if(l>=r)return ;
    int m=(l+r)>>1;
    cdq_1(l,m);cdq_1(m+1,r);
    int cnt=l-1;
    int i,j;
    for( i=l,j=m+1;j<=r;j++)
    {
        while(a[i].x<=a[j].x && i<=m)
        {
            a[i].flag=1;
            b[++cnt]=a[i++];
        }
        a[j].flag=2;
        b[++cnt]=a[j];
    }
    while(i<=m)b[++cnt]=a[i++],b[cnt].flag=1;
    while(j<=r)b[++cnt]=a[j++],b[cnt].flag=2;
    for(int i=l;i<=r;i++)a[i]=b[i];
    cdq_2(l,r) ;
}

int ans[N];
int main()
{
    int n;
    while(cin>>n)
    {
        memset(ans,0,sizeof(ans));
        memset(ou,0,sizeof(ou));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].x,&a[i].y);
            a[i].flag=0;
            a[i].id=i;
            ans[i-1]=0;
        }
        cdq_1(1,n);
        for(int i=1;i<=n;i++)
        {
            ans[ou[a[i].id]]++;
        }
        for(int i=0;i<n;i++)printf("%d\n",ans[i]);
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值