CDQ分治笔记
以前一直不会CDQ……然后经常听到dalao们说“这题直接CDQ啊”“CDQ不就秒了吗”的时候我只能瑟瑟发抖QAQ
CDQ分治
其实CDQ分治就是二分分治,每次将$[l,r]$的问题划分为$[l,mid]$和$[mid+1,r]$的子问题来解决,裸的时间复杂度是$O(nlogn)$。但是cdq的特殊要求是区间左半边的操作不会影响右半边的操作,一般适用于多次询问以及需要维护多个维度关键值的问题。(其实这种题也可以写树套树&KD树,dalao们又把我碾在了地上QAQ)
注意:cdq经常要在中间给数组排序,要使用归并来保证复杂度,直接sort的话或多一个$log$(Orzhjw)
三维偏序问题
三维偏序的著名不用我说了吧。。。几乎是所有人cdq的入门题,会cdq的人应该都写过吧。。。
先从一维偏序问题开始考虑,这就是一个经典的逆序对问题,用树状数组解决;
二维偏序的话可以对第一维排序,保证这一维有序后再对第二维建树状数组维护;
问题变成了三维,排序+树状数组也只能解决两维,还有一维就要用cdq分治(树套树)来搞:
注意到第一维排序后前一半的答案不会影响后一半的答案,于是把两边的区间分别按照第二维排序,然后建个树状数组解决;
(yrx:树套树好写易懂好调肯定写树套树啊)
代码:(BZOJ3262陌上花开 模板题)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define lb(x) (x&(-x))
using namespace std;
struct task{
int a,b,c,tot,ans;
task(){
tot=;
}
}q[],s[];
int n,m,tot=,ans[],t[];
bool cmp(task a,task b){
if(a.a!=b.a)return a.a<b.a;
if(a.b!=b.b)return a.b<b.b;
if(a.c!=b.c)return a.c<b.c;
return false;
}
bool _cmp(task a,task b){
if(a.b!=b.b)return a.b<b.b;
if(a.c!=b.c)return a.c<b.c;
if(a.a!=b.a)return a.a<b.a;
return false;
}
void add(int x,int v){
for(;x<=m;x+=lb(x)){
t[x]+=v;
}
}
int sum(int x){
int ret=;
for(;x;x-=lb(x)){
ret+=t[x];
}
return ret;
}
void cdq(int l,int r){
if(l==r){
s[l].ans+=s[l].tot-;
return;
}
int mid=(l+r)/;
cdq(l,mid);
cdq(mid+,r);
sort(s+l,s+mid+,_cmp);
sort(s+mid+,s+r+,_cmp);
int j=l;
for(int i=mid+;i<=r;i++){
while(j<=mid&&s[j].b<=s[i].b)add(s[j].c,s[j].tot),j++;
s[i].ans+=sum(s[i].c);
}
for(int i=l;i<j;i++)add(s[i].c,-s[i].tot);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c);
q[i].ans=;
}
sort(q+,q+n+,cmp);
for(int i=;i<=n;i++){
if(i!=&&q[i].a==q[i-].a&&q[i].b==q[i-].b&&q[i].c==q[i-].c)s[tot].tot++;
else s[++tot]=q[i],s[tot].tot=;
}
cdq(,tot);
sort(s+,s+tot+,cmp);
for(int i=;i<=tot;i++)ans[s[i].ans]+=s[i].tot;
for(int i=;i<=n;i++)printf("%d\n",ans[i]);
return ;
}
(这个代码多了一个log跑的巨慢无比)
一些刷题记录:
【BZOJ2176】天使玩偶
KD树大法好
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define inf 1000000000
using namespace std;
struct node{
int d[],mi[],mx[],ls,rs;
}t[];
int D,n,m,rt,x,y,op,tot=,ans;
bool cmp(node a,node b){
return a.d[D]!=b.d[D]?a.d[D]<b.d[D]:a.d[D^]<b.d[D^];
}
void pushup(int u){
int l=t[u].ls,r=t[u].rs;
if(l){
t[u].mi[]=min(t[u].mi[],t[l].mi[]);
t[u].mi[]=min(t[u].mi[],t[l].mi[]);
t[u].mx[]=max(t[u].mx[],t[l].mx[]);
t[u].mx[]=max(t[u].mx[],t[l].mx[]);
}
if(r){
t[u].mi[]=min(t[u].mi[],t[r].mi[]);
t[u].mi[]=min(t[u].mi[],t[r].mi[]);
t[u].mx[]=max(t[u].mx[],t[r].mx[]);
t[u].mx[]=max(t[u].mx[],t[r].mx[]);
}
}
void build(int &u,int l,int r,int d){
D=d;
int mid=(l+r)/;
u=mid;
nth_element(t+l,t+u+,t+r+,cmp);
t[u].mi[]=t[u].mx[]=t[u].d[];
t[u].mi[]=t[u].mx[]=t[u].d[];
if(u>l)build(t[u].ls,l,mid-,D^);
if(u<r)build(t[u].rs,mid+,r,D^);
pushup(u);
}
void ins(int k){
int d=,now=rt;
for(;;){
t[now].mi[]=min(t[now].mi[],t[k].mi[]);
t[now].mi[]=min(t[now].mi[],t[k].mi[]);
t[now].mx[]=max(t[now].mx[],t[k].mx[]);
t[now].mx[]=max(t[now].mx[],t[k].mx[]);
if(t[k].d[d]>=t[now].d[d]){
if(!t[now].rs){
t[now].rs=k;
return;
}
now=t[now].rs;
}else{
if(!t[now].ls){
t[now].ls=k;
return;
}
now=t[now].ls;
}
d^=;
}
}
int dis(int u,int x,int y){
int ret=;
if(x<t[u].mi[])ret+=t[u].mi[]-x;
if(x>t[u].mx[])ret+=x-t[u].mx[];
if(y<t[u].mi[])ret+=t[u].mi[]-y;
if(y>t[u].mx[])ret+=y-t[u].mx[];
return ret;
}
int query(int u,int x,int y){
int l,r,now;
now=abs(t[u].d[]-x)+abs(t[u].d[]-y);
ans=min(ans,now);
if(t[u].ls)l=dis(t[u].ls,x,y);
else l=inf;
if(t[u].rs)r=dis(t[u].rs,x,y);
else r=inf;
if(l<r){
if(l<ans)query(t[u].ls,x,y);
if(r<ans)query(t[u].rs,x,y);
}else{
if(r<ans)query(t[u].rs,x,y);
if(l<ans)query(t[u].ls,x,y);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d%d",&t[i].d[],&t[i].d[]);
}
build(rt,,n,);
tot=n;
for(int i=;i<=m;i++){
scanf("%d%d%d",&op,&x,&y);
if(op==){
++tot;
t[tot].mi[]=t[tot].mx[]=t[tot].d[]=x;
t[tot].mi[]=t[tot].mx[]=t[tot].d[]=y;
ins(tot);
}else{
ans=inf;
query(rt,x,y);
printf("%d\n",ans);
}
}
return ;
}
【BZOJ1176】Mokia
下面一题的加强版,必须要写cdq
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define lb(x) (x&(-x))
using namespace std;
struct node{
int v,x,y,op,d,id;
}q[],tmp[];
int s,w,x,y,a,b,k,op,cnt=,tot=,aans[],ans[],t[];
bool cmp(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.op<b.op;
}
void add(int u,int x){
for(;u<=w;u+=lb(u)){
t[u]+=x;
}
}
int query(int u){
int ret=;
for(;u;u-=lb(u)){
ret+=t[u];
}
return ret;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)/,L=l,R=mid+;
for(int i=l;i<=r;i++){
if(q[i].v<=mid&&!q[i].op)add(q[i].y,q[i].d);
if(q[i].v>mid&&q[i].op)ans[q[i].id]+=query(q[i].y)*q[i].d;
}
for(int i=l;i<=r;i++){
if(q[i].v<=mid&&!q[i].op)add(q[i].y,-q[i].d);
}
for(int i=l;i<=r;i++){
if(q[i].v<=mid)tmp[L++]=q[i];
else tmp[R++]=q[i];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
cdq(l,mid);
cdq(mid+,r);
}
int main(){
scanf("%d%d",&s,&w);
for(;;){
scanf("%d",&op);
if(op==){
scanf("%d%d%d",&x,&y,&k);
q[++cnt]=(node){cnt,x,y,,k,};
}else if(op==){
scanf("%d%d%d%d",&x,&y,&a,&b);
tot++;
aans[tot]=s*(a-x+)*(b-y+);
q[++cnt]=(node){cnt,a,b,,,tot};
q[++cnt]=(node){cnt,a,y-,,-,tot};
q[++cnt]=(node){cnt,x-,b,,-,tot};
q[++cnt]=(node){cnt,x-,y-,,,tot};
}else break;
}
sort(q+,q+cnt+,cmp);
cdq(,cnt);
for(int i=;i<=tot;i++)printf("%d\n",ans[i]+aans[i]);
return ;
}
【BZOJ2683】简单题
KD树大法好(成功跑到垫底QAQ)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int D;
struct kdnode{
int ls,rs,num,v,d[],mi[],mx[];
int &operator [](int x){
return d[x];
}
friend bool operator <(kdnode a,kdnode b){
return a[D]<b[D];
}
}t[],rb[],s;
int n,op,x1,yy,x2,y2,v,rt=,tot=,R=,ans=;
bool inside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){
return x1<=x3&&x2>=x4&&yy<=y3&&y2>=y4;
}
bool outside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){
return x1>x4||x2<x3||yy>y4||y2<y3;
}
void pushup(int u){
int l=t[u].ls,r=t[u].rs;
for(int i=;i<=;i++){
t[u].mi[i]=t[u].mx[i]=t[u][i];
if(l)t[u].mi[i]=min(t[u].mi[i],t[l].mi[i]);
if(l)t[u].mx[i]=max(t[u].mx[i],t[l].mx[i]);
if(r)t[u].mi[i]=min(t[u].mi[i],t[r].mi[i]);
if(r)t[u].mx[i]=max(t[u].mx[i],t[r].mx[i]);
}
t[u].num=t[l].num+t[r].num+t[u].v;
}
int query(int u,int x1,int yy,int x2,int y2){
int ret=;
if(!u)return ;
if(inside(x1,yy,x2,y2,t[u].mi[],t[u].mi[],t[u].mx[],t[u].mx[]))return t[u].num;
if(outside(x1,yy,x2,y2,t[u].mi[],t[u].mi[],t[u].mx[],t[u].mx[]))return ;
if(inside(x1,yy,x2,y2,t[u][],t[u][],t[u][],t[u][]))ret+=t[u].v;
ret+=query(t[u].ls,x1,yy,x2,y2)+query(t[u].rs,x1,yy,x2,y2);
return ret;
}
void ins(int &u,bool d){
if(!u){
u=++tot;
t[u][]=t[u].mi[]=t[u].mx[]=s[];
t[u][]=t[u].mi[]=t[u].mx[]=s[];
}
if(s[]==t[u][]&&s[]==t[u][]){
t[u].v+=s.v;
t[u].num+=s.v;
return;
}
if(s[d]<t[u][d])ins(t[u].ls,d^);
else ins(t[u].rs,d^);
pushup(u);
}
int rebuild(int l,int r,bool d){
if(l>r)return ;
int mid=(l+r)/;
D=d;
nth_element(rb+l,rb+mid,rb+r+);
t[mid]=rb[mid];
t[mid].ls=rebuild(l,mid-,d^);
t[mid].rs=rebuild(mid+,r,d^);
pushup(mid);
return mid;
}
int main(){
scanf("%d",&n);
for(;;){
scanf("%d",&op);
if(op==){
scanf("%d%d%d",&x1,&yy,&v);
//x1^=ans;
//yy^=ans;
//v^=ans;
s[]=x1;
s[]=yy;
s.num=s.v=v;
ins(rt,);
//printf("aa %d\n",tot);
if(tot==R){
//printf("zjtywwakioi\n");
for(int j=;j<=tot;j++){
rb[j]=t[j];
}
rt=rebuild(,tot,);
R+=;
}
}else if(op==){
scanf("%d%d%d%d",&x1,&yy,&x2,&y2);
//x1^=ans;
//yy^=ans;
//x2^=ans;
//y2^=ans;
printf("%d\n",(ans=query(rt,x1,yy,x2,y2)));
}else break;
}
return ;
}
【BZOJ1492】【NOI2007】Cash
CDQ分治维护凸包+斜率优化DP
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define eps 1e-7
#define inf 1e15
using namespace std;
struct task{
double x,y,a,b,k,r;
int v,id;
}q[],tmp[];
double gtk(int a,int b){
if(!b)return -inf;
if(fabs(q[a].x-q[b].x)<eps)return inf;
return (q[a].y-q[b].y)/(q[a].x-q[b].x);
}
int n,top,s[];
double f[];
bool cmp(task a,task b){
return a.k>b.k;
}
void cdq(int l,int r){
if(l==r){
f[l]=max(f[l],f[l-]);
q[l].y=f[l]/(q[l].a*q[l].r+q[l].b);
q[l].x=q[l].r*q[l].y;
return;
}
int mid=(l+r)/,L=l,R=mid+,j=;
for(int i=l;i<=r;i++){
if(q[i].id<=mid)tmp[L++]=q[i];
else tmp[R++]=q[i];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
cdq(l,mid);
top=;
for(int i=l;i<=mid;i++){
while(top>&>k(s[top-],s[top])<gtk(s[top-],i)+eps)top--;
s[++top]=i;
}
s[++top]=;
for(int i=mid+;i<=r;i++){
while(j<top&>k(s[j],s[j+])+eps>q[i].k)j++;
f[q[i].id]=max(f[q[i].id],q[s[j]].x*q[i].a+q[s[j]].y*q[i].b);
}
cdq(mid+,r);
L=l,R=mid+;
for(int i=l;i<=r;i++){
if(((q[L].x<q[R].x||(fabs(q[L].x-q[R].x)<eps&&q[L].y<q[R].y))||R>r)&&L<=mid)tmp[i]=q[L++];
else tmp[i]=q[R++];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
}
int main(){
scanf("%d%lf",&n,&f[]);
for(int i=;i<=n;i++){
scanf("%lf%lf%lf",&q[i].a,&q[i].b,&q[i].r);
q[i].k=-q[i].a/q[i].b;
q[i].id=i;
}
sort(q+,q+n+,cmp);
cdq(,n);
printf("%.2lf",f[n]);
return ;
}
【HDU5126】stars
毒瘤题,四维CDQ or 三维KD树,先咕
CDQ分治笔记的更多相关文章
- BZOJ1173 CDQ分治 笔记
目录 二维数据结构->cdq 预备知识 T1: 二维树状数组 T2:cdq分治 bzoj1176 mokia:Debug心得 一类特殊的CDQ分治 附: bzoj mokia AC代码 二维数据 ...
- CDQ分治笔记+例题
CDQ分治是一种离线分治算法,它基于时间顺序对操作序列进行分治. 看这样一个问题: 在一个三维坐标系中,有若干个点,每个点都有对应的坐标 \((X_i , Y_i , Z_i)\) ,我们要对于每个点 ...
- cdq分治 笔记
算法讲解 这个算法用于解决三维偏序问题. 三维偏序:给定 \(n\) 个三元组: \((a_i,b_i,c_i)\),求同时满足满足 \(a_i\le a_j,b_i\le b_j,c_i\le c_ ...
- 一篇自己都看不懂的CDQ分治&整体二分学习笔记
作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...
- 学习笔记 | CDQ分治
目录 前言 啥是CDQ啊(它的基本思想) 例题 后记 参考博文 前言 博主太菜了 学习快一年的OI了 好像没有什么会的算法 更寒碜的是 学一样还不精一样TAT 如有什么错误请各位路过的大佬指出啊感谢! ...
- 【教程】简易CDQ分治教程&学习笔记
前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦! CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...
- [学习笔记] CDQ分治 从感性理解到彻底晕菜
最近学了一种叫做CDQ分治的东西...用于离线处理一系列操作与查询似乎跑得很快233 CDQ的名称似乎源于金牌选手陈丹琦 概述: 对于一坨操作和询问,分成两半,单独处理左半边和处理左半边对于右半边的影 ...
- [偏序关系与CDQ分治]【学习笔记】
组合数学真是太棒了 $CDQ$真是太棒了(雾 参考资料: 1.<组合数学> 2.论文 课件 很容易查到 3.sro __stdcall 偏序关系 关系: 集合$X$上的关系是$X$与$X$ ...
- 初学cdq分治学习笔记(可能有第二次的学习笔记)
前言骚话 本人蒟蒻,一开始看到模板题就非常的懵逼,链接,学到后面就越来越清楚了. 吐槽,cdq,超短裙分治....(尴尬) 正片开始 思想 和普通的分治,还是分而治之,但是有一点不一样的是一般的分治在 ...
随机推荐
- Ubuntu 16.04安装Caffe的记录及FCN官方代码的配置
相关内容搜集自官方文档与网络,既无创新性,也不求甚解,我也不了解Caffe,仅仅搭上之后做个记录,方便以后重装 安装依赖项sudo apt-get install libprotobuf-dev li ...
- LeetCode(15)3Sum
题目如下: Python代码: def threeSum(self, nums): res = [] nums.sort() for i in xrange(len(nums)-2): if i &g ...
- ZBrush中如何清除遮罩
在之前的学习中我们知道在ZBrush®中如何创建遮罩,在创建遮罩时怎样进行反转来选择反选遮罩,本文将详细讲解ZBrush中如何清除遮罩,当我们利用遮罩达到预期效果时就需要将遮罩清除了:或者在做了遮罩的 ...
- 第七章 Python之模块与包
模块介绍 一个模块就是包含了一组功能的python文件(例如module.py,模块名是module),它从文件级别组织程序,更方便管理,这时我们不仅仅可以把这些文件当作脚本执行,还可以把他们当作模块 ...
- 04 SqlServer
数据库的注释 –(两个横线) 主键表 外键表 主键,组合主键 SqlServer 使用附加 权限 主文件mdf 日志文件ldf 数据类型 char varchar nchar nvarchar cha ...
- Jmeter中模拟多用户执行多场景操作
1.其实一个用户组就是一个场景(Thread Group).可以在一个测试计划中进行多个场景的执行,在测试计划下加一个全局的User Defined Variables,在这个里面可以设置执行总数to ...
- IOS - PDF合并
#pragma mark - Merge PDF - (void)mergePDF { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSD ...
- BZOJ 1016 最小生成树计数(矩阵树定理)
我们把边从小到大排序,然后依次插入一种权值的边,然后把每一个联通块合并. 然后当一次插入的边不止一条时做矩阵树定理就行了.算出有多少种生成树就行了. 剩下的交给乘法原理. 实现一不小心就会让程序变得很 ...
- Docker学习总结(13)——从零开始搭建Jenkins+Docker自动化集成环境
本文只简单标记下大概的步骤,具体搭建各个部分的细节,还请自行搜索.第一.二部分只是对Jenkins和Docker的简单介绍,熟悉的同学请直接跳到第三部分. 一.关于Jenkins Jenkins简介 ...
- Java 学习(10):java 异常处理
java 异常处理 异常发生的原因有很多,通常包含以下几大类: 用户输入了非法数据. 要打开的文件不存在. 网络通信时连接中断,或者JVM内存溢出. 三种类型的异常: 检查性异常: 最具代表的检查性异 ...