[BZOJ3262] 陌上花开 —— CDQ三维分治

Description
有n朵花,每朵花有三个属性:花形(s)、颜色©、气味(m),用三个整数表示。
现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。
定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。
显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。
Input
第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性
Output
包含N行,分别表示评级为0…N-1的每级花的数量。

Sample Input
10 3

3 3 3

2 3 3

2 3 1

3 1 1

3 1 2

1 3 1

1 1 2

1 2 2

1 3 2

1 2 1
Sample Output
3

1

3

0

1

0

1

0

0

1

题意:

给你n朵花,每朵花有三个参数,判定一朵花比另一朵漂亮的条件是三个参数都大于等于另一朵的三个参数。漂亮值的意思是这朵花比多少花漂亮。最后叫你输出从0到n-1每种漂亮值有多少花。

题解:

第一次写CDQ分治,在刚开始了解这个的时候学的一脸懵逼,因为无论是网上的博客还是周围的人他们的CDQ分治写的都不尽相同,这给我的理解带来了一定的负担,在看了他们的代码之后发现是在递归的时候写的位置不同,我个人是推荐写在最后回溯的时候比较好,这个容易理解,因为如果是递归下去写的话,首先会想到排序的问题,第二维的排序如果用sort的话那就有点慢了,当然也有不用sort的方法,用所有离散化之后的mid来判断哪里修改,哪里记录,但是我感觉不太地道,也没学。如果是回溯的时候写代码的话,那么回溯回去的时候一定是排好序了,这个很容易理解。
我是这么写的,对于三维的问题,第一维sort排序,第二维CDQ归并排序,同时在处理第二维的时候,第三维用树状数组记录。对于这道题目我先sort,之后去重。在CDQ的时候二分要解决的区间,直到末尾,如果这个区间只有一个数,那就返回了,如果有两个数,那么看看这两个的大小,如果前面这一个数比较小,那么后面这个数当然要+1,这时候可能会有人有问题:万一后面的y特别大呢?之后不会在算吗?答案当然是不会的,因为我们归并上去的时候,只有左边是修改的,右边是查询的,因为我们按照x排序过,就算它的y特别大,也会到左边去。tmp数组是归并排序的临时数组,注意最后要清空树状数组。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct node
{
    int num,x,y,z,ans;
}flow[N],tmp[N];
bool cmp1(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;
}
int num[N*2];
int lowbit(int x){return x&(-x);}
void add(int x,int val)
{
    for(int i=x;i<N*2;i+=lowbit(i))
        num[i]+=val;
}
void clear(int x)
{
    for(int i=x;i<N*2;i+=lowbit(i))
        num[i]=0;
}
int query(int x)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i))
        sum+=num[i];
    return sum;
}
int fin[N];
void CDQ(int l,int r)
{
    if(r==l)
        return ;
    int mid=l+r>>1;
    CDQ(l,mid),CDQ(mid+1,r);
    int p1=l,p2=mid+1,t=0;
    while(p1<=mid&&p2<=r)
    {
        if(flow[p1].y<=flow[p2].y)
            add(flow[p1].z,flow[p1].num),tmp[++t]=flow[p1],p1++;
        else
            flow[p2].ans+=query(flow[p2].z),tmp[++t]=flow[p2],p2++;
    }
    while(p1<=mid)
        tmp[++t]=flow[p1],p1++;
    while(p2<=r)
        flow[p2].ans+=query(flow[p2].z),tmp[++t]=flow[p2],p2++;
    for(int i=1;i<=t;i++)
        clear(tmp[i].z),flow[l+i-1]=tmp[i];
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&flow[i].x,&flow[i].y,&flow[i].z);
    sort(flow+1,flow+1+n,cmp1);
    int all=0,cnt=0;
    for(int i=1;i<=n;i++)
    {
        cnt++;
        if(flow[i].x!=flow[i+1].x||flow[i].y!=flow[i+1].y||flow[i].z!=flow[i+1].z)
        {
            flow[++all]=flow[i];
            flow[all].num=cnt;
            cnt=0;
        }
    }
    CDQ(1,all);
    for(int i=1;i<=all;i++)
        fin[flow[i].ans+flow[i].num-1]+=flow[i].num;
    for(int i=0;i<n;i++)
        printf("%d\n",fin[i]);
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值