• CDQ分治入门 + 例题 Arnooks's Defensive Line [Uva live 5871]


    CDQ分治入门

    简介

    CDQ分治是一种特别的分治方法,它由CDQ(陈丹琦)神犇于09国家集训队作业中首次提出,因此得名。CDQ分治属于分治的一种。它一般只能处理非强制在线的问题,除此之外这个算法作为某些复杂算法的替代品几乎是没有缺点的。

    深入

           对于一个数据结构题而言(或者需要运用数据结构的地方),我们无非就是做两件操作,一是修改,二是查询。

           对于修改而言,有插入,删除,变更(其实等价于删除再插入)这几种方式。

           那么查询的本质是什么呢?我们思考所遇到过的数据结构题,可以发现查询实际上就在做一件事情:把符合本次查询的限制的修改对答案产生的效果合并起来满足。这种限制通常表现为一种序的要求,并且这种序是广义的,符合限制的操作往往是按某种序(或多种序)排序后的操作的前缀。        通常来说,查询一定有时间上的限制,也就是要求考虑发生在某个时刻之前的所有查询,对于一个问题而言,假如所有查询要求的发生时刻相同,那这就是一个静态查询问题,如果要求发生的时刻随着查询而变,那这就是一个动态修改问题,动态修改问题较静态查询而言复杂很多,往往需要高级数据结构,可持久化等手段,而静态查询简单很多,例如时间倒流,twopointers之类的方法都是很好的选择。

      动态修改->静态查询!

           CDQ分治算法的核心就在于:去掉时间的限制,将所有查询要求发生的时刻同化,化动态修改为静态查询(其实对于有些问题来说可以把某一维的限制通过排序看作时间限制然后运用CDQ分治)。       

      我们记过程Solve(l,r)表示处理完[l,r]内的修改对查询的影响。此时我们引入分治思想,将操作序列划分为[l,mid],[mid+1,r]两个区间,这两个/区间内部的修改对区间内部的查询的影响是完全相同的/的子问题,我们递归处理,处理完之后剩下来只要考虑[l,mid]中的修改对[mid+1,r]中的查询的影响。这时我们发现这其实已经变成了一个静态查询问题,因为所有的查询都发生在修改之后,我们只需要考虑静态查询的问题如何处理即可。

      具体做法

           考虑这样一个问题,有若干询问和若干修改,要求在O(nlogn)时间复杂度内回答所有的询问。(下把询问与修改统称为操作)。

      我们把[l,r]代表当前处理的操作的区间,即处理第l个到第r个操作,先找到区间的中间m=(l+r)/2。

        (1)然后对于前一半[l,m]我们先递归解决。

        (2)对于所有在[l,m]内的修改操作,枚举处理它对于[m,r]内的所有操作影响。

        (3)之后递归处理[m,r]这一区间。        

      复杂度分析:分治共有log(n)层,那么要求每一层都在线性的时间复杂度内完成,才能保证总时间复杂度是O(nlogn)。       

       对于不同的题目,修改和询问是不一样的。在某些修改之间互相独立的题目下,还可以调换(2)(3)的顺序。

      当然cdq分治也可以像其他的分治方法一样,巧妙利用归并排序,来实现每层O(logn)

    接下来我们看道题目帮助理解

    Arnooks's Defensive Line [Uva live 5871]

    Describe  (不想看英语的请往下跳到一句话题意)

      Based on the latest intelligence reports, Chief Arnook of the northern tribe has become suspicious of the warrior nations that dwell south of the border. The chief has ordered his warriors to protect the southern border which runs parallel to the 54 o latitude line and stretches between the longitudes of 1 o to 1000, 000, 000 o , inclusive.
      Each warrior is assigned the task of protecting a segment of the border defined to lie between longitudes a and b, inclusive. No two warriors are assigned to protect the exact same segment. Bound by loyalty to his chief, a warrior will inform the chief upon his arrival at his appointed post and will never leave once he arrives.
      Your task is to write a program that performs the following two operations + a b a warrior assumes his position and protects the segment between longitudes a and b, inclusive. ? c d computes the number of warriors who completely protect the segment between longitudes c and d, inclusive. The segment between the longitudes c and d, inclusive, is said to be completely protected by a warrior X if and only if warrior X protects a segment between a and b, inclusive, and a ≤ c < d ≤ b. to help Chief Arnook track the status of his border protection.

    Input
    The input starts with an integer N (1 ≤ N ≤ 500000), on a line by itself, that indicates the number of operations. Each of the following N lines contains one operation. The description of an operation consists of a character ‘+’ or ‘?’ followed by two integers on a line by itself. The entries on a line are separated by single blank spaces.

    Output
    There is one output line for each input line that starts with the operation ‘?’. The output consists of a single integer that represents the number of warriors who completely protect the corresponding segment at the time.
    There is no output for input lines that start with the character ‘+’.

    Sample Input
    9
    + 5 10
    + 7 20
    + 3 15
    ? 9 12
    + 10 20
    ? 8 9
    + 6 30
    ? 8 9
    ? 9 12

    Sample Output
    2
    3
    4
    3

    一句话题意

    有两种操作(共n个,n<=500000), 一个是插入一个[l,r]的区间,另一个是询问一个区间[l,r],前面有多少个区间完全包含了这个区间。

    思路

    法一:很容易想到的一个搞法是线段树套平衡树,也就是对于插入的区间,把[1,l]都插入一个r,然后对询问的区间就是看[1,l]里面有多少个数>=r,然而这样的空间复杂度是nlog2n,n=50w显然T飞

    法二:换种思路,去掉时间限制,把问题看成一个序列,则前面的修改会对后面的询问造成影响。于是我们便想到了CDQ分治中第二点:对于所有在[l,m]内的修改操作,枚举处理它对于[m,r]内的所有操作影响。”所以我们可以对一个操作序列[a,b], 设m=(a+b)/2,先递归处理[a,m], [m+1,b],然后考虑[a,m]里面的插入区间的操作对[m+1,b]里面询问的影响。也就是转化成静态的问题。可以先把区间按照右端点从大到小排序*,然后扫一边,扫到一个插入区间用树状数组就在l处+1,询问就是原有答案加上sum(l)。

    上面*号注意,右端点相同时,左端点一定要从小到大排序,举个例子,如果一个询问是 i+1->j,一个插入是i->j,插入一定要排在询问前面才能被统计到

    代码

     1 #include<set>
     2 #include<map>
     3 #include<stack>
     4 #include<queue>
     5 #include<cstdio>
     6 #include<cstring>
     7 #include<iostream>
     8 #include<algorithm>
     9 #define RG register int
    10 #define rep(i,a,b)    for(RG i=a;i<=b;i++)
    11 #define per(i,a,b)    for(RG i=a;i>=b;i--)
    12 #define ll long long
    13 #define inf (1<<30)
    14 #define maxn 500005
    15 #define lowbit(x) (x&(-x))
    16 using namespace std;
    17 int n,cnt;
    18 int hash_table[maxn<<1],t[maxn<<1],ans[maxn];
    19 struct Dat{
    20     char type;
    21     int l,r,id;
    22 }dat[maxn];
    23 inline int read()
    24 {
    25     int x=0,f=1;char c=getchar();
    26     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    27     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    28     return x*f;
    29 }
    30 
    31 int pre()
    32 {
    33     char c;
    34     n=read();
    35     rep(i,1,n)
    36     {
    37         scanf("%c",&c);dat[i].l=hash_table[++cnt]=read(),dat[i].r=hash_table[++cnt]=read(),dat[i].id=i;
    38         if(c=='+')    dat[i].type=1;
    39     }
    40     sort(hash_table+1,hash_table+1+cnt);
    41     cnt=unique(hash_table+1,hash_table+1+cnt)-hash_table-1;
    42     rep(i,1,n)
    43     {
    44         dat[i].l=lower_bound(hash_table+1,hash_table+1+cnt,dat[i].l)-hash_table;
    45         dat[i].r=lower_bound(hash_table+1,hash_table+1+cnt,dat[i].r)-hash_table;
    46     }
    47 }
    48 
    49 inline bool cmp(const Dat &a,const Dat &b){return a.r==b.r?a.l<b.l:a.r>b.r;}
    50 
    51 void add(int x,int val)
    52 {
    53     while(x<=cnt)
    54         t[x]+=val,x+=lowbit(x);
    55 }
    56 
    57 int query(int x)
    58 {
    59     int sum=0;
    60     while(x)
    61         sum+=t[x],x-=lowbit(x);
    62     return sum;
    63 }
    64 
    65 void CDQ(int l,int r)
    66 {
    67     if(l==r)    return;
    68     int mid=(l+r)>>1;
    69     CDQ(l,mid);
    70     CDQ(mid+1,r);
    71     sort(dat+l,dat+r+1,cmp);
    72     for(int i=l;i<=r;i++)
    73     {
    74         if(dat[i].type&&dat[i].id<=mid)    add(dat[i].l,1);
    75         if(!dat[i].type&&dat[i].id>mid)    ans[dat[i].id]+=query(dat[i].l);
    76     }
    77     for(int i=l;i<=r;i++)
    78         if(dat[i].type&&dat[i].id<=mid)    add(dat[i].l,-1);
    79 }
    80 
    81 inline bool cnm(const Dat &a,const Dat &b){return a.id<b.id;}
    82 
    83 int main()
    84 {
    85     pre();
    86     CDQ(1,n);
    87     sort(dat+1,dat+1+n,cnm);
    88     rep(i,1,n)    if(!dat[i].type)    printf("%d
    ",ans[i]);
    89     return 0;
    90 }
    View Code
  • 相关阅读:
    LinuxMCE
    qBittorrent 0.9.0
    Exaile 0.2.9
    GAdminHttpd:图形化的 Apache 打点对象
    FBReader-电子书阅读对象
    CSSED:Linux 下 Web 拓荒者的 CSS 编纂利器
    Canorus:乐谱编辑软件
    AutoScan-收集监视及办理器械
    Lunar Applet:在桌面表现阴历
    Totem 2.18.1
  • 原文地址:https://www.cnblogs.com/ibilllee/p/7678685.html
Copyright © 2020-2023  润新知