BZOJ1173 CDQ分治 笔记

二维数据结构->cdq

预备知识

偏序关系 link1

数据结构 link1, 进阶指南P212

杂烩 link1

T1: 二维树状数组

hdu1892
一维树状数组: 二进制分段思想

可以把\(m = 2^a+2^b+\cdots(a>b>\cdots)\) 分解成\((0, 2^a], (2^a , 2^a+2^b]...,(m - 2^z+1, m]\)这log个区间, 所以对于每个\(x\)\((x-lowbit(x), x]\)

二维树状数组也是这样, 每一行的元素维护的是之前\([x-lowbit(x)+1, x]\)列该位置元素的和

所以只要在普通树状数组上再加一维就好啦

T2:cdq分治

例子: 求逆序对(这个经典问题在很多OJ上都有)

有句话叫罗马不是一天建成的, cdq分治也不是凭空想出来的,该算法与 求逆序对的两种做法有很深的关联.

逆序对的定义是 一对数\((i, j),i<j, a[i]>a[j]\) 所以我们把求逆序对问题转换为统计在x之前比x大的数有多少个这一问题 这种问题是二维偏序问题的一种(意思是要统计满足两个序的元素个数)

第一种做法是使用树状数组, 可以使用权值树状数组在\(O(logn)\)的时间内在线维护比x大的个数

第二种做法就很妙了.

我们可以发现, 如果数字排好序了, 值就很方便算(为0). 但是问题是, 我们打破了题目的一个限定(i<j).

关键是, 我们不一定需要将所有数字都排好序啊!!! 我们只需要让满足\(i<j\) 的数在数\(j\)之前出现就可以了啊!!!

所以有一个解决方法是用分治来"去掉"\(i<j\)这个"序"所带来的不方便之处. 我们可以将数分成两半, 分别统计出每一半内的答案, 之后再排好序, 求出每一个数的答案, 然后算前面的数对后面数字答案的影响. 此时, 前面一半和后面一半的所有数之间, \(i<j\)这个"序是成立的. 而在每一半数内, 答案已经求出,所有我们就可以把这一半内数字的第一维"序"(\(i<j\))打乱, 按照第二维序(\(a_i<a_j\))排序. 这样我们就可以方便的求出答案啦!!

cdq分治也是一样. 把所有的修改/询问分成两份, 统计前一半区间内的修改对后一般区间内的查询造成的影响. 这样就相当于把所有的修改放在前面,询问放在后面,所以cdq分治可以将在线算法转换成离线的.

(其实离线算法就是可以忽略时间的影响直接对x或y排序的算法)

这样, cdq分治就可以对"偏序问题"进行"降维打击", 比如, 可以把二维线段树才能做的问题变成用扫描线就可以做的问题(虽然Hold不住强制在线...呜呜呜)

强不强大?

有几个小技巧:

  1. 统计答案时用指针把每一个询问用指针映射到数组, 方便统计答案.

  2. 见归并代码

        static op tmp[siz];
        for(int pos = l, i = l, j = mid+1; pos <= r; pos++){
            if((ops[i].x <= ops[j].x && i <= mid) || j > r){
                //将前一半数放入数据结构
                if(!isquery(ops[i])) add(ops[i].y, ops[i].a);
                tmp[pos] = ops[i];
                i++;
            } else {
                if(isquery(ops[j])) *(ops[j].ans) += query(ops[j].y) * ops[j].d;
                //指针
                tmp[pos] = ops[j];
                j++;
            }
        }    
    

    习题 : bzoj1176 mokia

bzoj1176 mokia:Debug心得

  1. 读入单个字符用cin 不用scanf
  2. 不要贸然进行一些不确定的优化

一类特殊的CDQ分治

例题:百度地图的实时路况(Jisuanke 11217)

给出伪代码 思路不写了

//处理: 如果floyd不考虑[l, r] 的话, dis数组会是怎么样
//将数组存入tmp_d[dep]中
void cdq(int l, int r, int dep){
    tmp_d[dep] = t
}

附: bzoj mokia AC代码

#include<bits/stdc++.h>
using namespace std;

template<typename T>
void read(T &ans){T f = 1; char c = getchar();ans = 0;while(!isdigit(c)){if(c == '-')f = -1;c = getchar();}while( isdigit(c)){ans = ans*10 + c - '0';c = getchar();}ans *= f;}

const int mxm = 16*1e4, mxq = 1e4, mxw = 2e6;
struct op{
    int typ;
    int x, y, a, d;
    int *ans;
}ops[mxm+mxq*4+1];

op getop(int x, int y, int a, int typ){
    op ret; ret.x = x, ret.y = y, ret.d = 23333;ret.a = a; ret.typ = 1;return ret;
}
op getop(int x, int y, int d, int *ans, int typ){
    op ret; ret.x = x, ret.y = y, ret.d = d, ret.a = 0;ret.ans = ans; ret.typ = 2;return ret;
}
bool isquery(op x){
    return x.typ == 2;
}
int s, w, ans[mxq+1];


int c[mxw+5];
int query(int x){
    int ret = 0;
    for(; x > 0; x -= x&(-x)) ret += c[x];
    return ret;
}
void add(int x, int v){
    for(; x <= mxw; x += x&(-x)) c[x] += v;
}
void clr(int x){
    for(; x <= mxw; x += x&(-x)) c[x] = 0;	
}
void work(int l, int r){
    if(l >= r) return;
    
    int mid = (l+r)>>1;
    work(l, mid); work(mid+1, r);

    static op tmp[mxm+mxq*4+1];
    for(int pos = l, i = l, j = mid+1; pos <= r; pos++){
        if((ops[i].x <= ops[j].x && i <= mid) || j > r){
            if(!isquery(ops[i])) add(ops[i].y, ops[i].a);
            // , printf("%d %d %d ..add\n", i, ops[i].y ,ops[i].a);
            tmp[pos] = ops[i];
            i++;
        } else {
            if(isquery(ops[j])) *(ops[j].ans) += query(ops[j].y) * ops[j].d;
				// , printf("%d %d %d\n", query(ops[j].y),ops[j].d, query(ops[j].y) * ops[j].d);
            tmp[pos] = ops[j];
            j++;
        }
    }    
//    puts("end.");
    for(int i = l; i <= r; i++){
        ops[i] = tmp[i];
        if(!isquery(ops[i])) clr(ops[i].y);
        
    }
    // for(int i = 1; i <= 10; i++)if(query(ops[i].y) != 0) printf("When l = %d, r = %d, FAAAA!!!! ON %d, get %d\n",l, r,i,(query(ops[i].y)));
}
signed main(){
//	freopen("1.in", "r", stdin);
//	freopen("1.out", "w", stdout);
    read(s), read(w);

    int op = 0, cnt = 0, qcnt = 0;
    int x, y, x2, y2, a;
    memset(ops, 0, sizeof ops);
    
//    cout<<query(4)<<endl;
    while(1){
        read(op); 
//       	cout<<op<<endl;
        if(op == 3) break;
        read(x), read(y);

        if(op == 1){
            read(a);
            ops[++cnt] = getop(x, y, a, 1);
        } else if(op == 2){
            read(x2), read(y2);
            qcnt++;
//            ans[qcnt] = (y2 - y + 1) * (x2 - x + 1) * s;
            ops[++cnt] = getop(x2, y-1, -1, ans+qcnt, 2);
            ops[++cnt] = getop(x-1, y2, -1, ans+qcnt, 2);
            ops[++cnt] = getop(x-1, y-1, 1, ans+qcnt, 2);
            ops[++cnt] = getop(x2, y2, 1, ans+qcnt, 2);
        }
    }
    // for (int i = 1; i <= cnt; i++) {
    //     printf("%d:Triple(x = %d, y = %d, a = %d, d = %d)\n",ops[i].typ,ops[i].x, ops[i].y, ops[i].a, ops[i].d);
    // }
    work(1, cnt);


    for(int i = 1; i <= qcnt; i++) {
        cout << ans[i] << endl;
    }
    return 0;
}
/*
0 200
1 76 150 1
1 158 170 2
1 12 4 123
2 92 59 153 141
3
*/
posted @ 2018-10-30 20:58  懿路智行  阅读(278)  评论(0编辑  收藏  举报