[学习笔记] CDQ分治

听娜姐讲完FFT,一脸懵逼,还是来讲讲【CDQ分治】吧。。。

用途:

解决 “带时间轴的更改和查询” 问题。

算法流程:

首先我们要知道,这个算法是离线的,还是利用递归进行求解的。

  1. 把读入的n个操作都按照时间轴排列好。
  2. 把时间轴劈开,分为[l,mid]和[mid+1,r]两部分。
  3. 开始当前层cdq(l,r)的求解前先进行cdq(l,mid)与cdq(mid+1,r)的求解。
  4. 拎出区间[l,mid]内的所有修改操作和区间[mid+1,r]内的所有查询操作[mid+1,r]。
  5. 统计区间[l,mid]内的所有修改操作对区间[mid+1,r]内的所有查询操作的影响,并将影响加入ans数组中。

关于算法的正确性:

为什么这么做会是对的呢?我们不妨举个例子来看看。

加入一共有7个时刻(或者说是个7操作),对于位于3时刻的修改操作,在cdq(1,7)中,统计了它对于区间[5,7]中查询的影响,又在cdq(3,4)中统计了区间[4,4]中查询的影响。

可以看到,对于位于3时刻的查询操作,在我们的cdq过程中,统计了它对于[4,7]的时刻中,所有查询操作的影响。

例题以及题解:

例题:

BOI 2007 【Mokia 摩基亚】     洛谷里有,P4390。

题目概述:

(至于为什么是概述呢?——题目B话太TM多了!!!

给你一个二维平面直角坐标系,还有一些操作。

操作有两种:

1.格式"(1,x,y,w)",表示在(x,y)的地方加上权值w。
2.格式"(2,sx,sy,ex,ey)",表示询问以(sx,sy)为左上角顶点,(ex,ey)为右下角顶点,询问矩形区间内的权值和。

后面的修改操作不会对前面的查询操作产生影响。

数据:操作数<=200000,x,y,sx,sy,ex,ey<=2000000

思路分析:

大家还是先打消要用二维树状数组的念头吧

是二维的,那么我们应该怎么做呢?(先提示下吧,log2的,大家可以先想一想)

滴,滴,滴,滴————————

好,时间到,那就再给大家一个提示吧。

某名Niro**的dalao(他/她 真的是dalao)说过:“CDQ分治就是花log n的时间,把所有的修改操作都移到查询操作之前。”

再想想。。。。。。

(假如说还没有想到的话,可以先看一下下文中那些大号加粗的字,看看有没有思路啥的)

(想到了的话,就可以把这篇博客关掉了,再顺手点个推荐啥的

嗯,现在就到我讲了吧!

二维的,又不能用二维树状数组,再看看博客标题——“CDQ分治”。

en~~~~~

那就肯定是CDQ分治啦!!!

那么当我们把所有的修改都放在查询后面之后,我们应该怎么做呢?

显然要对问题进行“降维打击”

这个降维打击是个啥呢?——就是把1、2操作(其中把2操作变成起始和结束的两个点)对于x轴进行排序嘛。

排序之后,我们就可以保证在x轴上,前面做过的修改操作,在x轴上是永远包含于后面的查询操作的(如果它的x坐标大于当前查询操作右端点对应的左端点的)。

那么我们只要维护y轴就可以了,这个可以用树状数组来维护。

具体方法的话就是,给树状数组中修改点的y坐标加上它的权值,然后遇到查询操作的右端点时,就统计一下y取值范围内的和就可以了。

那么最后的一个问题就是如果修改点的x坐标小于查询当前查询操作右端点对应的左端点我们应该怎么办呢?

大家先思考一下吧。

......

~~~好了吗?我说喽。——差分嘛!

具体方法就是——遇到左端点时,先把当前树状数组中属于询问范围的y的区间和给减掉嘛。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
struct point{
    int x,y,id;
};
point a[N],b[N<<1];
int x[N],y[N],sx[N],sy[N],ex[N],ey[N],w[N],ans[N],c[2000005],acnt,bcnt,tot,opt;
bool cmp (point a,point b){
    return a.x<b.x;
}
void update(int x,int val){
    if (x<=2000000) c[x]+=val,update(x+(x&(-x)),val);
}
int query(int x){
    if (x>0) return c[x]+query(x-(x&(-x))); else return 0;
}
void cdq(int l,int r){
    if (l==r) return;
    int mid=l+r>>1; 
    cdq(l,mid); cdq(mid+1,r);
    acnt=0,bcnt=0;
    for (int i=l;i<=mid;i++)
        if (x[i]>0) ++acnt,a[acnt].x=x[i],a[acnt].y=y[i],a[acnt].id=i;
    for (int i=mid+1;i<=r;i++)
        if (sx[i]>0) 
            ++bcnt,b[bcnt].x=sx[i]-1,b[bcnt].y=sy[i],b[bcnt].id=i,
            ++bcnt,b[bcnt].x=ex[i],b[bcnt].y=ey[i],b[bcnt].id=i;
    sort(a+1,a+1+acnt,cmp);
    sort(b+1,b+1+bcnt,cmp);
    int i=1,j=1;
    while (i<=acnt||j<=bcnt)
        if (j>bcnt||i<=acnt&&a[i].x<=b[j].x) 
            update(a[i].y,w[a[i].id]),++i;
        else 
            if (b[j].x==sx[b[j].id]-1&&b[j].y==sy[b[j].id]) 
                ans[b[j].id]-=query(ey[b[j].id])-query(b[j].y-1),j++;
            else 
                ans[b[j].id]+=query(b[j].y)-query(sy[b[j].id]-1),j++;
    for (int i=l;i<=mid;i++) 
        if (x[i]>0) update(y[i],-w[i]);
}
int main(){
    scanf("0 %d",&w[0]);
    scanf("%d",&opt);
    while (opt!=3){
        if (opt==1) ++tot,scanf("%d%d%d",&x[tot],&y[tot],&w[tot]);
        else ++tot,scanf("%d%d%d%d",&sx[tot],&sy[tot],&ex[tot],&ey[tot]);
        scanf("%d",&opt);
    }
    cdq(1,tot);
    for (int i=1;i<=tot;i++)
        if (sx[i]>0) printf("%d
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/WR-Eternity/p/10079431.html