当前位置: 首页 > news >正文

CDQ整体二分-三维偏序(陌上花开)

题面

本文讲cdq整体二分的思路与做法。=分治VS数据结构

其实维度这一方面,空间几何可以是维度,像时间这样有规定顺序的词语也可能是维度。

cdq

三维偏序,一般可以用一维一维的消。可以用cdq嵌套,线段树嵌套来做,我还不会,这里用排序一维,cdq一维,树状数组一维,感觉对比嵌套在思维拓展方面有帮助。

题面的 f [ i ] f[i] f[i]满足 a [ j ] ≤ a [ i ] , b [ j ] ≤ b [ i ] , c [ j ] ≤ c [ i ] a[j]\leq a[i],b[j]\leq b[i],c[j]\leq c[i] a[j]a[i],b[j]b[i],c[j]c[i],因为元素相同的情况在分治中会出现漏解,所以cdq分治在出现相同元素时常做去重处理。

做法:

先按去重后序列以a为第一关键字 注意不是唯一关键字 排序,

然后常规分治,之后将左右区间分别按照b为(第一)关键字排序,

这时因为我们要根据左区间给右区间贡献,而左区间的a值一定都小于右区间的a值,所以a无影响。按照b归并排序的同时更新树状数组,并得到右区间的贡献最后记得清空树状数组

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#define M (l+r>>1)
using namespace std;
const int N=1e5+5;
int n,k,tot,ans[N],tr[N<<1];
struct qh{
    int a,b,c,ans,cnt;
}in[N],a[N];
bool c1(qh a,qh b){return a.a==b.a?a.b==b.b?a.c<b.c:a.b<b.b:a.a<b.a;}
bool c2(qh a,qh b){return a.b==b.b?a.c<b.c:a.b<b.b;}
int lowbit(int x){return x&(-x);}
void add(int x,int y){for(int i=x;i<=k;i+=lowbit(i)) tr[i]+=y;return ;}
int gs(int x){int res=0;for(int i=x;i;i-=lowbit(i)) res+=tr[i];return res;}
void cdq(int l,int r){
    if(l==r) return ;
    cdq(l,M);cdq(M+1,r);
    sort(a+l,a+M+1,c2);sort(a+M+1,a+r+1,c2);
    int q1=l,q2=M+1;
    while (q1<=M&&q2<=r){
        if(a[q1].b<=a[q2].b){
            add(a[q1].c,a[q1].cnt);
            q1++;
        }
        else{
            a[q2].ans+=gs(a[q2].c);
            q2++;
        }
    }
    while (q2<=r){
        a[q2].ans+=gs(a[q2].c);
        q2++;
    }
    for(int i=l;i<q1;i++) add(a[i].c,-a[i].cnt);        //*******
    return ;
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&in[i].a,&in[i].b,&in[i].c);
    sort(in+1,in+n+1,c1);
    int c=0;
    for(int i=1;i<=n;i++){
        c++;
        if(in[i].a!=in[i+1].a||in[i].b!=in[i+1].b||in[i].c!=in[i+1].c) a[++tot]=(qh){in[i].a,in[i].b,in[i].c,0,c},c=0;
    }
    cdq(1,tot);
    for(int i=1;i<=tot;i++) ans[a[i].ans+a[i].cnt-1]+=a[i].cnt;
    for(int i=0;i<n;i++) printf("%d\n",ans[i]);
    return 0;
}
//cdq分治:相同元素做去重处理

整体二分

例题

题目大意:

给定一个长度为 n ( n <= 50,000 ) 的数组 a[1] , a[2] … a[n] 和 q ( q <= 10,000 ) 此询问,每次询问:

  1. Q i j k 表示区间 [i,j] 中第 k 小的数是多少,并输出这个数

  2. C i t 表示将第 i 个数改为 t

样例输入

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

样例输出

3
6

整体二分,顾名思义,就是把所有的东西拿来一起二分。在这道题里我们还要开一棵树状数组。

  1. 修改操作等于删除+添加
  2. 查询操作=二分答案
  3. 修改和加入按照所在位置二分
  4. 顺序处理所有操作

如果哪一步不懂就看代码吧。

#include<cstdio>
#include<iostream>
#define M (l+r>>1)
using namespace std;
const int N=5e4+5;
int n,q,tot,qtot,a[N],ans[N],tr[N],cnt[N<<1];
struct qh{
    int x,y,z,t,ad,s;
}E[N<<1],El[N<<1],Er[N<<1],op;
inline int Rd(){
    int s=0,w=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
    return s*w;
}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int y){
    for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=y;
    return ;
}
inline int gs(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tr[i];
    return res;
}
inline void init(int h,int t,int l,int r){
    if(h>t) return ;
    if(l==r){                    //pd l==r
        for(int i=h;i<=t;i++) if(E[i].t==3) ans[E[i].ad]=l;
        return ;
    }
    for(int i=h;i<=t;i++){       //ls
        if(E[i].t==1&&E[i].y<=M) add(E[i].x,1);
        else if(E[i].t==2&&E[i].y<=M) add(E[i].x,-1);
        else if(E[i].t==3) cnt[i]=gs(E[i].y)-gs(E[i].x-1);
    }
    for(int i=h;i<=t;i++){       //empty
        if(E[i].t==1&&E[i].y<=M) add(E[i].x,-1);
        else if(E[i].t==2&&E[i].y<=M) add(E[i].x,1);
    }
    int l1=0,r1=0;
    for(int i=h;i<=t;i++){       //merge
        if(E[i].t==3){
            if(E[i].s+cnt[i]>=E[i].z) El[l1++]=E[i];
            else E[i].s+=cnt[i],Er[r1++]=E[i];
        }
        else if(E[i].y<=M) El[l1++]=E[i];
        else Er[r1++]=E[i];
    }
    for(int i=0;i<l1;i++) E[h+i]=El[i];
    for(int i=0;i<r1;i++) E[h+l1+i]=Er[i];
    init(h,h+l1-1,l,M);
    init(h+l1,t,M+1,r);
}
int main(){
    n=Rd();q=Rd();
    for(int i=1;i<=n;i++){
        a[i]=Rd();
        op.x=i;op.y=a[i];op.t=1;
        E[++tot]=op;
    }
    while (q--){
        char ch=getchar();
        int x,y,z;
        while (ch!='Q'&&ch!='C') ch=getchar();
        if(ch=='Q'){
            x=Rd();y=Rd();z=Rd();
            op.x=x;op.y=y;op.z=z;op.t=3;op.ad=++qtot;
            E[++tot]=op;
        }
        else{
            x=Rd();y=Rd();
            op.x=x;op.y=a[x];op.t=2;E[++tot]=op;
            op.x=x;op.y=y;op.t=1;E[++tot]=op;
            a[x]=y;
        }
    }
    init(0,tot,0,1e9);
    for(int i=1;i<=qtot;i++) printf("%d\n",ans[i]);
    return 0;
}

结构跟权值线段树还挺像

总结

整体二分的 思想 是同时对处理区间和答案进行二分。

CDQ分治的 **思想 **是用处理方式(关键字)进行排序,然后对时间进行二分。

整体二分 可以用于询问操作一样,而且可以二分答案解决的问题(具有单调性,如时间可以二分)。

CDQ分治 可以用于 求多维偏序问题。

整体二分从上往下做,CDQ分治从下往上做。

两种分治算法都比较暴力,它们的优点是代码短而清晰,缺点是复杂度玄学,必须离线。

所以,这次还是没能决出胜负啊。

相关文章:

  • Vue3+elementplus搭建通用管理系统实例十三:添加树形选择器及多选功能
  • GBASE 8s 高可用配置参数
  • 大白话paxos raft
  • 微信小程序开发入门与实战(插槽及组件页面的生命周期)
  • QT 语言的学习 day09 进程 和 线程
  • Golang-02Golang变量与基本数据类型
  • 在线五子棋对战 --- 人机对战的实现
  • 【微信小程序】shrio安全登录界面实现
  • Apache网页的优化,安全与防盗链
  • python中Try的运用及意义
  • React中实现插槽效果的方案
  • 一起Talk Android吧(第三百八十九回:介绍两种实现倒计时的方法)
  • SystemVerilog——线程以及线程之间的通信
  • Node.js 应用开发详解开篇词 Node.j 从工程化工具到后端服务应用的转变
  • 【Android】Android Binder进程间通信AIDL示例与源码分析
  • Android Volley源码解析
  • IDEA常用插件整理
  • JavaScript类型识别
  • 测试开发系类之接口自动化测试
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 前端临床手札——文件上传
  • 前端面试题总结
  • 如何借助 NoSQL 提高 JPA 应用性能
  • Mac 上flink的安装与启动
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​TypeScript都不会用,也敢说会前端?
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • #includecmath
  • #每日一题合集#牛客JZ23-JZ33
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (1)bark-ml
  • (6)设计一个TimeMap
  • (C语言)逆序输出字符串
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (六)vue-router+UI组件库
  • (生成器)yield与(迭代器)generator
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET Micro Framework初体验(二)
  • .NET 中的轻量级线程安全
  • .net反编译的九款神器
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...
  • [2013AAA]On a fractional nonlinear hyperbolic equation arising from relative theory
  • [20170728]oracle保留字.txt
  • [bbk5179]第66集 第7章 - 数据库的维护 03
  • [BZOJ3211]:花神游历各国(小清新线段树)
  • [codevs 2822] 爱在心中 【tarjan 算法】
  • [Erlang 0129] Erlang 杂记 VI 2014年10月28日
  • [Gamma]阶段测试报告
  • [GN] Vue3.2 快速上手 ---- 核心语法2
  • [JS]JavaScript 简介