bzoj 3262: 陌上花开 cdq分治

题目

Description

有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(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

HINT

1 <= N <= 100,000, 1 <= K <= 200,000

cdq分治

这题跟想象中的cdq分治是差不多的,比较好理解。

引用网上的话:第一维排序,第二维cdq分治,第三维树状数组。
也就是先按第一维进行排序,然后把第二维cdq分治一下,每次先往下cdq(l,mid),cdq(mid+1,r),由于cdq结束之后的第二维是有序的,那么就可以用树状数组统计前半部分对后半部分的贡献了。
但还有个问题就是如果有两个所有参数都相同的花怎么办。我参考的是网上的办法:所有相同的都只算一朵就好了,但在处理的时候还是要把所有的都算进去。

总结起来其原理就是跟归并排序差不多。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 200005
using namespace std;

int c[M],n,m,ans[N];
struct data{int x,y,z,ans,size;}q[N],t[N],f[N];

bool cmp(data a,data b)
{
    return a.x<b.x||a.x==b.x&&a.y<b.y||a.x==b.x&&a.y==b.y&&a.z<b.z;
}

void ins(int x,int y)
{
    while (x<=m)
    {
        c[x]+=y;
        x+=x&(-x);
    }
}

int query(int x)
{
    int ans=0;
    while (x)
    {
        ans+=c[x];
        x-=x&(-x);
    }
    return ans;
}

void cdq(int l,int r)
{
    if (l>=r) return;
    int mid=(l+r)/2;
    cdq(l,mid);cdq(mid+1,r);
    int i=l;
    for (int j=mid+1;j<=r;j++)
    {
        while (q[i].y<=q[j].y&&i<=mid) 
        {
            ins(q[i].z,q[i].size);
            i++;
        }
        q[j].ans+=query(q[j].z);
    }
    i=l;
    for (int j=mid+1;j<=r;j++)
        while (q[i].y<=q[j].y&&i<=mid)
        {
            ins(q[i].z,-q[i].size);
            i++;
        }
    i=l;int j=mid+1,k=l;
    while (i<=mid&&j<=r)
        if (q[i].y<q[j].y) t[k++]=q[i++];
        else t[k++]=q[j++];
    while (i<=mid) t[k++]=q[i++];
    while (j<=r) t[k++]=q[j++];
    for (int i=l;i<=r;i++)
        q[i]=t[i];
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z);
    sort(f+1,f+n+1,cmp);
    int size=0,tot=0;
    for (int i=1;i<=n;i++)
    {
        size++;
        if (f[i].x!=f[i+1].x||f[i].y!=f[i+1].y||f[i].z!=f[i+1].z)
        {
            q[++tot]=f[i];
            q[tot].size=size;
            size=0;
        }
    }
    cdq(1,tot);
    for (int i=1;i<=tot;i++)
        ans[q[i].ans+q[i].size-1]+=q[i].size;
    for (int i=0;i<n;i++)
        printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值