zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ",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)
    ",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
    */
    
  • 相关阅读:
    css3中calc()使用
    垂直居中
    QLineEdit IP地址校验
    UML类图几种关系的总结(网摘)
    如何解压 Mac OS X 下的 PKG 文件(网摘)
    %appdata%目录下配置文件修改
    文件字符串替换
    Qt版权符号显示问题
    Mac OS X 终端命令开启功能
    Qt 无边框拖拽实现
  • 原文地址:https://www.cnblogs.com/Eroad/p/9879525.html
Copyright © 2011-2022 走看看