CDQ分治属于比较特殊的一类分治,许多问题转化为这类分治的时候,时空方面都会有很大节省,而且写起来没有这么麻烦。

这类分治的特殊性在于分治的左右两部分的合并,作用两部分在合并的时候作用是不同的,比如,通过左半部分的影响来更新右半部分,所以分治开始前都要按照某一个关键字排序,然后利用这个顺序,考虑一个区间[l, r]的两部分间的影响。感觉说的太多,还是不如具体题目分析,而且题目也不尽相同,记住几句话是没什么用的。

练习地址:

http://vjudge.net/contest/view.action?cid=55322#overview

Problem A HYSBZ 3110 K大数查询

这个是一个很经典的树套树题目,看别人博客发现CDQ分治能够很好处理。

题意:有n个位置编号1~n,m个操作,每个操作两种类型:1 a b c 表示将第a~b个位置之间的每个位置插入一个数c;2 a b c 查询第a~b个位置之间的所有数中,第c大的数。

 

范围:

N,M<=50000,N,M<=50000


a<=b<=N

 

1操作中abs(c)<=N

2操作中abs(c)<=Maxlongint

分析:

按照CDQ分治的做法,是答案当做关键字来分治,由于答案最终在-n~n之间,这里首先需要一个转化,将区间第c大变成第c小,只需要将每个数变成n-c+1。

对于这类操作类的题目 ,CDQ分治的做法首先要保证的是操作的顺序,接下来以答案为关键字,例如询问结果在L~R之间的操作2,分成两部分递归L~m,m+1~R处理,#11对于操作1如果添加的数<=m,则加入到相应的位置区间;#12否则说明操作1影响答案在右半区间m+1~R的操作2。然后对于每个操作2查询当前位置区间有多少个数,表示该区间<=m已经有多少个数(#21),如果(#22)数目tmp > c (查询数目),说明答案应该在m+1~R,否则在L~m。然后将操作1中影响答案在左半部分的(编号#11)和操作2中答案在左半部分的(#21)集中在一起左半部分,剩下的集中在右半部分。然后递归处理答案在左半部分和右半部分的。每次进行子区间的递归时都将操作分成了2部分,表示不同区间被对应不同的操作。

具体成段增加一个值和查询某一段的和用到了树状数组,也可以用线段树,不过我觉得树状数组解法简洁有力,orz,

上一下原文树状数组链接

http://www.cnblogs.com/lazycal/archive/2013/08/05/3239304.html

我的代码:

bzoj好像不能用cout输出一个表达式,会RE!

  1 /*Time 2014 08 31 , 19:26
  2 
  3 */
  4 #include <bits/stdc++.h>
  5 #define in freopen("solve_in.txt", "r", stdin);
  6 #define bug(x) printf("Line %d : >>>>>>>\n", (x));
  7 
  8 using namespace std;
  9 typedef long long LL;
 10 const int maxn = 50000 + 100;
 11 LL x1[maxn][2], x2[maxn][2], ans[maxn];
 12 int cnt;
 13 
 14 struct Node
 15 {
 16     int l, r, type;
 17     LL c;
 18     int id;
 19 } q[maxn];
 20 int rk[maxn], t1[maxn], t2[maxn];
 21 int n, m;
 22 LL query(LL a[][2], int x)
 23 {
 24     LL res = 0;
 25     for(; x > 0; x -= (x&(-x)))
 26     {
 27         if(a[x][0] == cnt) res += a[x][1];
 28     }
 29     return res;
 30 }
 31 LL query(int l, int r)
 32 {
 33     return query(x1, l)*(r-l+1)+ (r+1)*(query(x1, r)-query(x1, l)) - (query(x2, r)-query(x2, l));
 34 }
 35 void add(LL a[][2], int x, LL c)
 36 {
 37     for(; x <= n; x += ((-x)&x))
 38     {
 39         if(a[x][0] == cnt) a[x][1] += c;
 40         else a[x][0] = cnt, a[x][1] = c;
 41     }
 42 }
 43 void add(int l, int r, int c)
 44 {
 45     add(x1, l, c);
 46     add(x2, l, (LL)l*c);
 47     add(x1, r+1, -c);
 48     add(x2, r+1, (LL)(r+1)*(-c));
 49 }
 50 void solve(int ll, int rr, int l, int r)
 51 {
 52     if(l > r) return;
 53     if(ll == rr)
 54     {
 55         for(int i = l; i <= r; i++)
 56             if(q[rk[i]].type == 2)
 57             {
 58                 ans[rk[i]] = ll;
 59             }
 60         return;
 61     }
 62     int m1 = (ll+rr)>>1, m2 = (l+r)>>1;
 63     cnt++;
 64     t1[0] = t2[0] = 0;
 65     for(int i = l; i <= r; i++)
 66     {
 67         if(q[rk[i]].type == 1)
 68         {
 69             if(q[rk[i]].c <= m1)
 70             {
 71                 add(q[rk[i]].l, q[rk[i]].r, 1);
 72                 t1[++t1[0]] = rk[i];
 73             }
 74             else
 75             {
 76                 t2[++t2[0]] = rk[i];
 77             }
 78         }
 79         else
 80         {
 81             LL xx = query(q[rk[i]].l, q[rk[i]].r);
 82             if(xx < (LL)q[rk[i]].c)
 83             {
 84                 q[rk[i]].c -= xx;
 85                 t2[++t2[0]] = rk[i];
 86             }
 87             else
 88             {
 89                 t1[++t1[0]] = rk[i];
 90             }
 91         }
 92     }
 93     m2 = l+t1[0]-1;
 94 
 95     for(int i = l; i <= r; i++)
 96     {
 97         if(i <= m2)
 98         {
 99             rk[i] = t1[i-l+1];
100         }
101         else
102         {
103             rk[i] = t2[i-m2];
104         }
105     }
106     solve(ll, m1, l, m2);
107     solve(m1+1, rr, m2+1, r);
108 }
109 int main()
110 {
111 
112     scanf("%d%d", &n, &m);
113     for(int i = 1; i <= m; i++)
114     {
115         rk[i] = i;
116         scanf("%d%d%d%lld", &q[i].type, &q[i].l, &q[i].r, &q[i].c);
117         if(q[i].type == 1) q[i].c = (LL)n-q[i].c+1;
118         q[i].id = i;
119     }
120     solve(1, 2*n+1, 1, m);
121     for(int i = 1; i <= m; i++)
122     {
123         if(q[i].type == 2)
124         {
125            printf("%d\n", n-ans[i]+1);
126         }
127     }
128     return 0;
129 }
View Code

相关文章: