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,超短裙分治....(尴尬) 正片开始 思想 和普通的分治,还是分而治之,但是有一点不一样的是一般的分治在 ...
随机推荐
- Powerpivot PowerBI相关组件下载安装(附操作截图)
加载方式:com加载项加载方法: 点击Excel界面[文件]→[选项]→[加载项]→[COM加载项]→[转到] Excel2013加载PowerView Excel216PowerQuery不需要加载 ...
- Book---强连通分量
这几天一直在做强连通,现在总结一小下 1.定义 在一个有向图中,如果任意的两个点都是相互可达的,就说这个图是强连通的,有向图的极大强连通子图,称为强连通分量 2.求法 学的是白书上的tarjan算法 ...
- RabbitMQ学习笔记(3)----RabbitMQ Worker的使用
1. Woker队列结构图 这里表示一个生产者生产了消息发送到队列中,但是确有两个消费者在消费同一个队列中的消息. 2. 创建一个生产者 Producer如下: package com.wangx.r ...
- Hihocoder1350-Binary Watch
时间限制:10000ms单点时限:1000ms内存限制:256MB 描述 Consider a binary watch with 5 binary digits to display hours ( ...
- [读书笔记] Python数据分析 (四) 数组和矢量计算
Numpy:高性能计算和数学分析的基础包 ndarray, 一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组 用于对数组数据进行快速运算的标准数学函数 用于读写磁盘数据的工具和用于操作内存 ...
- vue懒加载实现
- Vue 做项目经验
Vue 做项目经验 首先需要知道最基本的东西是: Vue 项目打包:npm run build Vue生成在网页上看的端口:npm run dev 修改端口号的地方在: config文件夹下index ...
- Spring MVC学习总结(7)——Spring MVC整合Ehcache缓存框架
Ehcache算是当前比较流行的缓存框架,使用缓存可以极大的缓解服务器和数据库的压力,提高访问效率,提高服务器的并发能力.接下来我们看怎么把缓存使用起来. SpringMVC集成Ehcache所需的j ...
- TCP的可靠传输(依赖流量控制、拥塞控制、连续ARQ)
TCP可靠性表现在它向应用层提供的数据是无差错,有序,无丢失,即递交的和发送的数据是一样的. 可靠性依赖于流量控制.拥塞控制.连续ARQ等技术 <TCP/IP详解>中的“分组”是不是就是报 ...
- HTML页面直接显示json 结构
<html> <head> <meta http-equiv="Content-Type" content="text/html; char ...