BZOJ 3262: 陌上花开 (CDQ分治)

本文介绍了解决BZOJ3262问题的方法,该问题涉及三维偏序处理,通过使用CDQ分治策略并结合树状数组进行优化,实现了高效求解每朵花的美丽评级。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ 3262: 陌上花开

题意概述

有N朵花,对于每一朵花,有三个属性: s ,c, m .
当且仅当si>sj,ci>cj,mi>mj,有花i比花j美丽.
一朵花的评级为比其他花更美丽的数量(不包括自己),输出评级为 0 N1 的花的数量.

题目分析:

初学CDQ分治,写了一下午.orz……

这是一个三维偏序的问题,可以用CDQ来搞一搞.
当然此题当中,一朵花既是修改操作,又是询问操作.
先以一维s从小到大排序,开始CDQ分治.
对于每一次完成两个子问题后,合并时,先按照c从小到大排序,第三维m用树状数组维护,对于当前花,若是左区间的花,树状数组add操作,否则,查询m比其小的花的个数.

(千万要注意三个属性均相同的花的处理,一种方法是先去重,再计算)

代码:

//Continue
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=100000+10;

int read() {
    char ch=getchar();int ret=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret;
}

struct Flower {
    int s,c,m,k,cnt,ans;//k:0 add操作,1 sum操作 
    //cnt记录这种花有多少,ans指的是这种花的评级 
    Flower(int cnt=0,int ans=0){}
    bool operator < (const Flower& rhs) const {
        return c<rhs.c||(c==rhs.c&&(m<rhs.m||(m==rhs.m&&s<rhs.s)));
    }
    bool operator == (const Flower& rhs) const {
        return s==rhs.s&&c==rhs.c&&m==rhs.m;
    }
    void input() {
        s=read();c=read();m=read();
    }
}t[maxn],f[maxn];

#define lowbit(x) (x&-x)

int bit[maxn],N,K;

void add(int d,int v) {
    for(;d<=K;d+=lowbit(d)) bit[d]+=v;
}

int sum(int d,int ret=0) {
    for(;d;d-=lowbit(d)) ret+=bit[d];
    return ret;
}

#define mid ((l+r)>>1)

int cmp(const Flower& a,const Flower& b) {
    return a.s<b.s||(a.s==b.s&&(a.c<b.c||(a.c==b.c&&a.m<b.m)));
}

void solve(int l,int r) {
    if(l==r) return ;
    //先递归解决左右子区间内部更新 
    solve(l,mid);
    solve(mid+1,r);
    //此时解决左右区间间的更新 
    for(int i=l;i<=mid;i++) f[i].k=0;//左区间修改 
    for(int i=mid+1;i<=r;i++) f[i].k=1;//右区间查询 

    sort(f+l,f+r+1);//按照c排序 
    for(int i=l;i<=r;i++)
        if(f[i].k) f[i].ans+=sum(f[i].m);//右区间查询 
        else add(f[i].m,f[i].cnt);//左区间修改 
    //注意在用分治求解的时候不能引入序列长度的时间复杂度,否则会造成时间复杂度退化 
    for(int i=l;i<=r;i++) if(!f[i].k) add(f[i].m,-f[i].cnt);//采用倒着修改回去的方法 
}

int ans[maxn],tot;

int main() {
    N=read();K=read();
    for(int i=1;i<=N;i++) t[i].input();

    sort(t+1,t+N+1,cmp);
    for(int i=1;i<=N;i++)//先去重操作 
        if(i==1||!(t[i]==t[i-1])) f[++tot]=t[i],f[tot].cnt=1;
        else ++f[tot].cnt;

    for(int i=1;i<=tot;i++) f[i].ans=f[i].cnt-1;//一开始评级赋值为个数-1 
    sort(f+1,f+tot+1,cmp);//按s排序 
    solve(1,tot);

    for(int i=1;i<=tot;i++) ans[f[i].ans]+=f[i].cnt;
    for(int i=0;i<N;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值