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不住强制在线...呜呜呜)
强不强大?
有几个小技巧:
-
统计答案时用指针把每一个询问用指针映射到数组, 方便统计答案.
-
见归并代码
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心得
- 读入单个字符用cin 不用scanf
- 不要贸然进行一些不确定的优化
一类特殊的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
*/