cdq分治(bzoj 1176: [Balkan2007]Mokia && bzoj 2683: 简单题)
2017-08-30 23:24
435 查看
CDQ分治:
本质:对询问进行分治
优点:和莫队分块一样都属于技巧,关键时刻能免去复杂的数据结构,常数小
缺点:必须离线
参考:http://blog.csdn.net/hbhcy98/article/details/50642773
http://www.cnblogs.com/mlystdcall/p/6219421.html
问题提出:
假设有数十万个操作,每次可以是修改和查询,可以这样理解:
对于第x个查询,只有前面的部分操作会对该查询有影响,而部分操作没有影响,
例如一个空的坐标系,操作C(x, y)表示添加一个点(x, y),Q(x, y)表示查询在(0, 0)到(x, y)这个范围内有多少个点
那么C(1, 2)会让Q(3, 5)的答案+1,C(4, 1)却对Q(3, 5)没有影响
这样每次查询的答案相当于之前若干次操作的叠加!
思考:
那么理论上只要对于每个询问,只要知道有多少个操作对它有贡献就行了,
而很容易发现,很好判定一个操作是否对某次查询有贡献,就像上面坐标系的例子:
对于C(x, y)和Q(x', y'),①满足x<=x'就可能有贡献,但只要x>x'就一定不可能有贡献,这就相当于是一个“序”
所以可以先将所有的C和Q作为一个整体按x排序
但是不一定,对于C(x, y)和Q(x', y'),还必须满足②y<=y',这就相当于第二个"序",也就是说按x排完序之后前面满足条件y<=y'的C(x,
y)的数量才是Q(x', y')的答案,考虑暴力,遇到一个C(x, y)就让cnt[y]++,遇到一个Q(x', y')的话查询cnt[]的前缀和,答案就是∑cnt[i] (0<=i<=y),这是经典问题,可以用树状数组解决
其实到这里对于先给n个加点操作,再给m个查询操作的问题就可以解决了!
但是万一加点操作和查询操作(也就是C和Q)混在一起呢?这样就相当于有第三个“序”,在满足x<'x'和y<=y'的条件下还要满足这个C出现在这个Q之前!令C(x, y, s)表示在第s秒添加一个点(x, y),Q(x',
y', s')表示查询在s'秒之前(0, 0)到(x, y)这个范围内有多少个点,第三维又该如何解决呢?
解决:
这就是“三维偏序问题”,第一维直接排序,第二维可以树状数组,第三维就必须用CDQ分治了
参考题目:bzoj 3262:陌上花开(三维偏序裸题)
当然CDQ分治本质当然还是分治,只不过是对操作和询问的分治,就拿上面例子,大致步骤如下:
①对于所有的C(x, y, s)、Q(x', y', s')当然还是按x排序(y仍然用树状数组暴力)
②假设共有n个操作+查询,开始二分,第一步当然是整个区间[1, n]
③m = (1+n)/2,将整个区间分为左右两个部分,更新y用树状数组就不说了,所有在[1, m]区间内满足s<m的C操作对所有在(m,
n]范围内满足s'>m的Q询问有贡献,计算这个贡献,然后重排,在保证x有序的情况下将所有满足s<=m的全部丢在左区间,将所有s>m+1的全部丢在右区间,递归区间[1, m]和(m, n],重复步骤③
④结束,输出答案!
这样问题就解决了,复杂度O(nlog²n)
下面看题
Submit: 1565 Solved: 632
[Submit][Status][Discuss]
接下来每行一个操作。
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
5
没错就是上面例子中的那道题,题解已经给出了
那就看程序吧,可以将每个查询拆成四个(0, 0)到(x, y)的部分
本质:对询问进行分治
优点:和莫队分块一样都属于技巧,关键时刻能免去复杂的数据结构,常数小
缺点:必须离线
参考:http://blog.csdn.net/hbhcy98/article/details/50642773
http://www.cnblogs.com/mlystdcall/p/6219421.html
问题提出:
假设有数十万个操作,每次可以是修改和查询,可以这样理解:
对于第x个查询,只有前面的部分操作会对该查询有影响,而部分操作没有影响,
例如一个空的坐标系,操作C(x, y)表示添加一个点(x, y),Q(x, y)表示查询在(0, 0)到(x, y)这个范围内有多少个点
那么C(1, 2)会让Q(3, 5)的答案+1,C(4, 1)却对Q(3, 5)没有影响
这样每次查询的答案相当于之前若干次操作的叠加!
思考:
那么理论上只要对于每个询问,只要知道有多少个操作对它有贡献就行了,
而很容易发现,很好判定一个操作是否对某次查询有贡献,就像上面坐标系的例子:
对于C(x, y)和Q(x', y'),①满足x<=x'就可能有贡献,但只要x>x'就一定不可能有贡献,这就相当于是一个“序”
所以可以先将所有的C和Q作为一个整体按x排序
但是不一定,对于C(x, y)和Q(x', y'),还必须满足②y<=y',这就相当于第二个"序",也就是说按x排完序之后前面满足条件y<=y'的C(x,
y)的数量才是Q(x', y')的答案,考虑暴力,遇到一个C(x, y)就让cnt[y]++,遇到一个Q(x', y')的话查询cnt[]的前缀和,答案就是∑cnt[i] (0<=i<=y),这是经典问题,可以用树状数组解决
其实到这里对于先给n个加点操作,再给m个查询操作的问题就可以解决了!
但是万一加点操作和查询操作(也就是C和Q)混在一起呢?这样就相当于有第三个“序”,在满足x<'x'和y<=y'的条件下还要满足这个C出现在这个Q之前!令C(x, y, s)表示在第s秒添加一个点(x, y),Q(x',
y', s')表示查询在s'秒之前(0, 0)到(x, y)这个范围内有多少个点,第三维又该如何解决呢?
解决:
这就是“三维偏序问题”,第一维直接排序,第二维可以树状数组,第三维就必须用CDQ分治了
参考题目:bzoj 3262:陌上花开(三维偏序裸题)
当然CDQ分治本质当然还是分治,只不过是对操作和询问的分治,就拿上面例子,大致步骤如下:
①对于所有的C(x, y, s)、Q(x', y', s')当然还是按x排序(y仍然用树状数组暴力)
②假设共有n个操作+查询,开始二分,第一步当然是整个区间[1, n]
③m = (1+n)/2,将整个区间分为左右两个部分,更新y用树状数组就不说了,所有在[1, m]区间内满足s<m的C操作对所有在(m,
n]范围内满足s'>m的Q询问有贡献,计算这个贡献,然后重排,在保证x有序的情况下将所有满足s<=m的全部丢在左区间,将所有s>m+1的全部丢在右区间,递归区间[1, m]和(m, n],重复步骤③
④结束,输出答案!
这样问题就解决了,复杂度O(nlog²n)
下面看题
2683: 简单题
Time Limit: 50 Sec Memory Limit: 128 MBSubmit: 1565 Solved: 632
[Submit][Status][Discuss]
Description
你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:命令 | 参数限制 | 内容 |
1 x y A | 1<=x,y<=N,A是正整数 | 将格子x,y里的数字加上A |
2 x1 y1 x2 y2 | 1<=x1<= x2<=N 1<=y1<= y2<=N | 输出x1 y1 x2 y2这个矩形内的数字和 |
3 | 无 | 终止程序 |
Input
输入文件第一行一个正整数N。接下来每行一个操作。
Output
对于每个2操作,输出一个对应的答案。Sample Input
41 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
35
没错就是上面例子中的那道题,题解已经给出了
那就看程序吧,可以将每个查询拆成四个(0, 0)到(x, y)的部分
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int n, t, z, tre[500005], ans[200005]; typedef struct Res { int x, y, val, opt, id, xd; bool operator < (const Res &b) const { if(x<b.x || x==b.x && y<b.y || x==b.x && y==b.y && opt<b.opt) return 1; return 0; } }Res; Res s[800005], L[400005], R[400005]; void Update(int x, int val) { while(x<=n) { tre[x] += val; x += x&-x; } } int Query(int x) { int ans = 0; while(x) { ans += tre[x]; x -= x&-x; } return ans; } void Cdq(int l, int r) { int m, i, p, q; if(l==r) return; m = (l+r)/2; for(i=l;i<=r;i++) { if(s[i].opt==1 && s[i].id<=m) Update(s[i].y, s[i].val); if(s[i].opt==2 && s[i].id>m) ans[s[i].xd] += Query(s[i].y)*s[i].val; } for(i=l;i<=r;i++) { if(s[i].opt==1 && s[i].id<=m) Update(s[i].y, -s[i].val); } p = q = 0; for(i=l;i<=r;i++) { if(s[i].id<=m) L[++p] = s[i]; else R[++q] = s[i]; } for(i=l;i<=m;i++) s[i] = L[i-(l-1)]; for(i=m+1;i<=r;i++) s[i] = R[i-m]; Cdq(l, m); Cdq(m+1, r); } int main(void) { int i, opt, x1, y1, x2, y2; scanf("%d", &n); while(1) { scanf("%d", &opt); if(opt==3) break; else if(opt==1) { t++; scanf("%d%d%d", &s[t].x, &s[t].y, &s[t].val); s[t].opt = 1, s[t].id = t; } else { z++; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); t++, s[t].x = x1-1, s[t].y = y1-1, s[t].xd = z, s[t].id = t, s[t].val = 1, s[t].opt = 2; t++, s[t].x = x2, s[t].y = y2, s[t].xd = z, s[t].id = t, s[t].val = 1, s[t].opt = 2; t++, s[t].x = x1-1, s[t].y = y2, s[t].xd = z, s[t].id = t, s[t].val = -1, s[t].opt = 2; t++, s[t].x = x2, s[t].y = y1-1, s[t].xd = z, s[t].id = t, s[t].val = -1, s[t].opt = 2; } } sort(s+1, s+t+1); Cdq(1, t); for(i=1;i<=z;i++) printf("%d\n", ans[i]); return 0; }
相关文章推荐
- BZOJ_2683_简单题&&BZOJ_1176_[Balkan2007]Mokia_CDQ分治+树状数组
- bzoj 1176: [Balkan2007]Mokia&&2683: 简单题 -- cdq分治
- BZOJ 1176: [Balkan2007]Mokia&&2683: 简单题|cdq分治
- 【BZOJ1176】[Balkan2007]Mokia/【BZOJ2683】简单题 cdq分治
- BZOJ1176: [Balkan2007]Mokia(BZOJ2683: 简单题)
- 【BZOJ-1176&2683】Mokia&简单题 CDQ分治
- [BZOJ1176] [Balkan2007]Mokia/[BZOJ2683] 简单题
- [BZOJ2683]简单题/[BZOJ1176][BalkanOI2007]Mokia
- 【BZOJ1176】【BOI2007】Mokia & 【BZOJ2683】简单题(CDQ分治+树状数组)
- 【bzoj1176】[Balkan2007]Mokia/【bzoj2683】简单题 CDQ分治+树状数组
- 【BZOJ 1176】 [Balkan2007]Mokia cdq分治
- [BZOJ1176][Balkan2007]Mokia && CDQ分治+树状数组
- [BZOJ1176] [Balkan2007]Mokia
- BZOJ1176: [Balkan2007]Mokia
- bzoj1176 [Balkan2007]Mokia
- 【BZOJ 1176】【Balkan 2007】Mokia
- BZOJ 1176: [Balkan2007]Mokia
- BZOJ 1176: [Balkan2007]Mokia( CDQ分治 + 树状数组 )
- BZOJ 1176: [Balkan2007]Mokia
- Bzoj1176 [Balkan2007]Mokia