bzoj 1176: [Balkan2007]Mokia (CDQ 分治, 两种方法的 )

阅读: 评论:0

bzoj 1176: [Balkan2007]Mokia (CDQ 分治, 两种方法的 )

bzoj 1176: [Balkan2007]Mokia (CDQ 分治, 两种方法的 )

描述:

维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

思路:

三维,时间一维,x轴一维,y轴一维,

按照一维排序之后,然后用左区间更新右区间;树状数组进行维护.

这里有两种写法,

一种是 用时间分治,一种是用 x 轴分治,

用时间分治:

这种用时间分治的方法,在CDQ中不需要在排序了, 这样子省下了不少时间.

为什么不需要排序?

我们先按照x轴排序,时间在区间里是.乱序的,然后时间在 [l,mid] 中的算左区间,[mid+1, r] 的算右区间.

区间 从 l 到 r ,那么时间也是从 l 到 r, 当我们递归每一层的时候,要把时间和 l , r 对应上.

#include<bits/stdc++.h>
#define low(x) (x & (-x))
using namespace std;
const int N = 2e5+100;
const int M = 2e6+10;
struct node{int id,qid,x,y,c;
}f[N*4],g[N*4];
bool cmp(node A, node B){if (A.x != B.x) return A.x < B.x;if (A.y != B.y) return A.y < B.y;return A.id < B.id;
}
int n,m,s,tim,qtim,ans[N];
int c[M];
void add(int x, int y){for (; x <= m; x += low(x)) c[x] += y;
}
int sum(int x){int ans = 0;for (; x; x -= low(x)) ans += c[x];return ans;
}
void CDQ(int l, int r){if (l >= r) return;int mid = (l + r) >> 1;for (int i = l; i <= r; ++i){if (f[i].id <= mid && f[i].qid == -1) add(f[i].y,f[i].c); elseif (f[i].id > mid && f[i].qid != -1) ans[f[i].qid] += f[i].c*sum(f[i].y);}for (int i = l; i <= r; ++i)if (f[i].id <= mid && f[i].qid == -1) add(f[i].y,-f[i].c);int x = l, y = mid; for (int i = l; i <= r; ++i)if (f[i].id <= mid) g[x++] = f[i]; else g[++y] = f[i];for (int i = l; i <= r; ++i)f[i] = g[i];CDQ(l,mid); CDQ(mid + 1, r); 
}
int main(){scanf("%d%d",&s,&m);int op,x,y,xx,yy,z;while(scanf("%d",&op)){if (op == 3) break;if (op == 1){scanf("%d%d%d",&x,&y,&z);f[++tim] = (node){tim,-1,x,y,z};} else{scanf("%d%d%d%d",&x,&y,&xx,&yy);f[++tim] = (node){tim,qtim,xx,yy,1};f[++tim] = (node){tim,qtim,x-1,yy,-1};f[++tim] = (node){tim,qtim,xx,y-1,-1};f[++tim] = (node){tim,qtim++,x-1,y-1,1};}}sort(f+1,f+tim+1,cmp);CDQ(1,tim);for (int i = 0; i < qtim; ++i)printf("%dn",ans[i]);return 0;
}/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
*/

 

 第二种写法实现就时间长了一点,

因为在 cdq中排序了,在 cdq 中,我们就是 [l,mid] 是左区间, [mid+1,r] 是右区间.用左区间去更新右区间,

然后这个时候是有序的,我们要以 x 轴为关键字排序,然后打乱区间.再进行更新.

#include<bits/stdc++.h>
#define low(x) (x & (-x))
using namespace std;
const int N = 2e5+100;
const int M = 2e6+10;
struct node{int id,qid,x,y,c;bool vis;
}f[N*4],g[N*4];
bool cmp(node A, node B){if (A.x != B.x) return A.x < B.x;if (A.y != B.y) return A.y < B.y;return A.vis < B.vis;
}
int n,m,s,tim,qtim,ans[N];
int c[M];
void add(int x, int y){for (; x <= m; x += low(x)) c[x] += y;
}
int sum(int x){int ans = 0;for (; x; x -= low(x)) ans += c[x];return ans;
}
void CDQ(int l, int r){if (l >= r) return;int mid = (l + r) >> 1;for (int i = l; i <= mid; ++i)g[i] = f[i], g[i].vis = 0;for (int i = mid+1; i <= r; ++i)g[i] = f[i], g[i].vis = 1;sort(g+l,g+r+1,cmp);for (int i = l; i <= r; ++i)if (g[i].vis == 0 && g[i].qid == -1) add(g[i].y,g[i].c); elseif (g[i].vis == 1 && g[i].qid != -1) ans[g[i].qid] += g[i].c*sum(g[i].y);for (int i = l; i <= r; ++i)if (g[i].vis == 0 && g[i].qid == -1) add(g[i].y,-g[i].c);CDQ(l,mid); CDQ(mid + 1, r); 
}int main(){scanf("%d%d",&s,&m);int op,x,y,xx,yy,z;while(scanf("%d",&op)){if (op == 3) break;if (op == 1){scanf("%d%d%d",&x,&y,&z);f[++tim] = (node){tim,-1,x,y,z};} else{scanf("%d%d%d%d",&x,&y,&xx,&yy);f[++tim] = (node){tim,qtim,xx,yy,1};f[++tim] = (node){tim,qtim,x-1,yy,-1};f[++tim] = (node){tim,qtim,xx,y-1,-1};f[++tim] = (node){tim,qtim++,x-1,y-1,1};}}CDQ(1,tim);for (int i = 0; i < qtim; ++i)printf("%dn",ans[i]);return 0;
}/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3*/

总结一下:

自我认为,第二种方法,是一个通用的方法,

然后第一种方法,分治的数组的值,一定要不重复,而且对应区间.

本文发布于:2024-02-04 22:18:43,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170717683560124.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:两种   方法   bzoj   CDQ   Mokia
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23