BZOJ3262:陌上花开(CDQ分治 + BIT)

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

 

Source

思路:三维偏序问题考虑cdq分治,cdq分治和普通的分治不同在于多维护了一个类似归并排序的过程。进行cdq分治之前要保证区间其中一边的操作不能影响另一边的,那么操作就要按照某些特征排列了,比如时间顺序。在本题中排序就要对x、y、z的优先级排序,排完序就能保证右边的不对左边的有贡献了(x、y、z全部一样的先不讨论)。那么左边的哪些对右边的哪些有贡献呢?总不能暴力二重循环扫一遍吧,此时对y的归并排序就发挥作用了,具体看代码。最后xyz相同的肯定排在一块,取他们中的最大值就行。

# include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+30;
int n, k, s[maxn], ans[maxn], tot[maxn];
struct node{
    int x, y, z, id;
}a[maxn], tmp[maxn];
bool cmp(node i, node j){
    if(i.x != j.x) return i.x <j.x;
    else{
        if(i.y != j.y) return i.y < j.y;
        else return i.z < j.z;
    }
}
void update(int pos){
    for(int i=pos; i<=k; i+=i&-i) ++s[i];
}
int cal(int pos){
    int res = 0;
    for(;pos>0;pos-=pos&-pos) res += s[pos];
    return res;
}
void clr(int pos){
    for(; pos<=k; pos+=pos&-pos){
        if(s[pos]) s[pos] = 0;
        else break;
    }
}
void cdq(int l, int r){
    if(l == r) return;
    int mid = l+r>>1, cnt=0;
    cdq(l, mid);
    cdq(mid+1, r);
    int L=l, R=mid+1;
    while(L<=mid && R<=r){
        if(a[L].y <= a[R].y){
            update(a[L].z);
            tmp[cnt++] = a[L++];
        }
        else{
            ans[a[R].id] += cal(a[R].z);
            tmp[cnt++] = a[R++];
        }
    }
    while(L <= mid) tmp[cnt++] = a[L++];
    while(R <= r){
        ans[a[R].id] += cal(a[R].z);
        tmp[cnt++] = a[R++];
    }
    for(int i=0; i<cnt; ++i){
        clr(tmp[i].z);
        a[l+i] = tmp[i];
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int  i=1; i<=n; ++i) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z), a[i].id=i;
    sort(a+1, a+1+n, cmp);
    cdq(1,n);
    for(int i=n-1; i; --i){
        if(a[i].x==a[i+1].x && a[i].y==a[i+1].y && a[i].z == a[i+1].z)
            ans[a[i].id] = max(ans[a[i].id], ans[a[i+1].id]);
    }
    for(int i=1; i<=n; ++i) ++tot[ans[i]];
    for(int i=0; i<n; ++i) printf("%d\n",tot[i]);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值