【BZOJ】1176: [Balkan2007]Mokia(cdq分治)

http://www.lydsy.com/JudgeOnline/problem.php?id=1176

在写这题的时候思维非常逗啊。。。。。。。。2333。。。。。。。。。。。。。。。。。。。

最后不得不去看别人的代码。。

噗,,我怎么没想到二维前缀和。。。。。。。。。。。。。。。。。。。。

orz zyf

那么对于一个矩形,我们拆成四个点,那么就可以和add操作一起cdq分治!

orz

cdq分治的话很好想的:

定义$solve(l, r)$表示用l~mid来更新mid+1~r。

考虑如何$O(n)$更新:

首先我们可以先考虑x轴,如果是已经排序好了的话,直接扫过去更新y轴!这点利用了单调的思想。

而如何更新y轴呢?树状数组。

而拆点后如何维护二维前缀和呢?加加减减。

如何快速排序呢?在外边先排序过。

然后完了。

我之前写的cdq非常逗啊,,都没有拆点,直接单调队列暴力扫,但是为啥wa了?我对拍的时候看了一下答案,只有一些答案错了QAQ。。。

一些技巧请看代码,orz

(丧心病狂の加速,我们离散y轴再用bit维护233)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define error(x) (!(x)?puts("error"):0)
#define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }

const int N=170005, M=2000005;

int tot, c[M];
inline void add(int x, const int &s) { for(; x<=tot; x+=x&-x) c[x]+=s; }
inline int sum(int x) { int ret=0; for(; x; x-=x&-x) ret+=c[x]; return ret; }

int y[N*4], W, n, cnt, ans[N], S;

struct dat { int x, y, id, pos, a; bool f; }p[N*5], t[N*5];
inline const bool cmp(const dat &a, const dat &b) { return a.x==b.x ? (a.y==b.y?a.pos<b.pos:a.y<b.y) : a.x<b.x; }

void cdq(int l, int r) {
	if(l==r) return;
	int mid=(l+r)>>1, l1=l, l2=mid+1;
	for1(i, l, r) {
		if(p[i].id<=mid && !p[i].f) add(p[i].y, p[i].a);
		if(p[i].id>mid && p[i].f) ans[p[i].pos]+=p[i].a*sum(p[i].y);
	}
	for1(i, l, r) if(p[i].id<=mid && !p[i].f) add(p[i].y, -p[i].a);
	for1(i, l, r) if(p[i].id<=mid) t[l1++]=p[i]; else t[l2++]=p[i];
	for1(i, l, r) p[i]=t[i];
	cdq(l, mid); cdq(mid+1, r);
}

int main() {
	read(S); read(W);
	int t=getint();
	while(t!=3) {
		if(t==1) { ++n; read(p[n].x); read(p[n].y); read(p[n].a); p[n].f=0; p[n].pos=0; p[n].id=n; y[++tot]=p[n].y; }
		else {
			int x1=getint(), y1=getint(), x2=getint(), y2=getint();
			ans[++cnt]=S*(x2-x1+1)*(y2-y1+1);
			p[++n].x=x1-1, p[n].y=y1-1, p[n].f=1, p[n].a=1; p[n].id=n; p[n].pos=cnt;
			p[++n].x=x1-1, p[n].y=y2, p[n].f=1, p[n].a=-1; p[n].id=n; p[n].pos=cnt;
			p[++n].x=x2, p[n].y=y1-1, p[n].f=1, p[n].a=-1; p[n].id=n; p[n].pos=cnt;
			p[++n].x=x2, p[n].y=y2, p[n].f=1, p[n].a=1; p[n].id=n; p[n].pos=cnt;
			y[++tot]=y1; y[++tot]=y2; y[++tot]=y1-1; y[++tot]=y2-1;
		}
		read(t);
	}
	sort(y+1, y+1+tot); tot=unique(y+1, y+1+tot)-y-1;
	for1(i, 1, n) p[i].y=lower_bound(y+1, y+1+tot, p[i].y)-y;
	sort(p+1, p+1+n, cmp);
	cdq(1, n);
	for1(i, 1, cnt) printf("%d\n", ans[i]);

	return 0;
}

  


Description

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

Input

第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小

接下来每行为一下三种输入之一(不包含引号):

"1 x y a"

"2 x1 y1 x2 y2"

"3"

输入1:你需要把(x,y)(第x行第y列)的格子权值增加a

输入2:你需要求出以左上角为(x1,y1),右下角为(x2,y2)的矩阵内所有格子的权值和,并输出

输入3:表示输入结束

Output

对于每个输入2,输出一行,即输入2的答案

Sample Input

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
5

HINT

保证答案不会超过int范围

Source

时间: 2024-10-20 16:21:37

【BZOJ】1176: [Balkan2007]Mokia(cdq分治)的相关文章

bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组

Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小 接下来每行为一下三种输入之一(不包含引号): "1 x y a" "2 x1 y1 x2 y2" "3" 输入1:你需要把(x,y)(第x行第y列)的格子权值增加a 输入

BZOJ 1176 [Balkan2007]Mokia CDQ分治

题目大意: 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. POJ1195的加强版 没记错的话上午这题还没有中文题目描述的说0.0 好迅速 首先这题看到W就知道二维树状数组挂了 看到M就发现离散化了也搞不了 0.0 这题似乎是CDQ分治被发现之后第二个解决的题目...不过只有会员才知道的世界,今天反应过来刷刷... 修改和询问放在一起分治,一个询问拆分成4个,树状数组处

BZOJ 1176: [Balkan2007]Mokia

一道CDQ分治的模板题,然而我De了一上午Bug...... 按时间分成左右两半,按x坐标排序然后把y坐标丢到树状数组里,扫一遍遇到左边的就add,遇到右边的query 几个弱智出了bug的点, 一是先分了左右两半再排序,保证的是这次的左右是上次没有计算过的贡献, for(int i=l;i<=r;i++) qs[i].k=(i>mid); sort(qs+l,qs+r+1,cmp2); 然后时间的先后是因为一开始就是按时间排好序的已经保证了. 二是矩阵的一个经典的套路就是拆成四部分差分查询.

BZOJ1176: [Balkan2007]Mokia CDQ分治

最近很不对啊=w= 写程序全是bug啊 ans数组开小了竟然一直不知道,小数据没问题大数据拍不过,交上去RE 蛋疼半天 这个主要把每次询问拆成3个询问. #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<iostream> #define dout printf using namespace std; const int Maxw=

[BZOJ1176][Balkan2007]Mokia cdq+树状数组

1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 3134  Solved: 1395[Submit][Status][Discuss] Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小

BZOJ 1176 Balkan 2007 Mokia CDQ分治

题目大意:有一些操作,给一个坐标代表的点加上一个数,和求出一个矩形中的所有数的和. 思路:一眼题,二位树状数组水过. ... .. . 哪里不对?W<=2000000.逗我?这n^2能开下? 这个时候CDQ神牛又来帮助我们了. 这个题应该算是CDQ分治的模板题了吧,简单分析一下,其实不难. 写这个题之前建议写一下BZOJ 1935 SHOI 2007 Tree 园丁的烦恼 树状数组这个题,是本题的简化版. 按照正常的解法,我们应该建立一个二位的数据结构,然后分别维护两维的信息.如果用动态开点的线

BZOJ 3963: [WF2011]MachineWorks [CDQ分治 斜率优化DP]

传送门 当然了WF的题uva hdu上也有 你的公司获得了一个厂房N天的使用权和一笔启动资金,你打算在这N天里租借机器进行生产来获得收益.可以租借的机器有M台.每台机器有四个参数D,P,R,G.你可以在第D天花费P的费用(当然,前提是你有至少P元)租借这台机器,从第D+1天起,操作机器将为你产生每天G的收益.在你不再需要机器时,可以将机器卖掉,一次性获得R的收益.厂房里只能停留一台机器.不能在购买和卖出机器的那天操作机器,但是可以在同一天卖掉一台机器再买入一台.在第N+1天,你必须卖掉手上的机器

BZOJ 2683 简单题 cdq分治+树状数组

题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用. 首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响. 我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这

BZOJ 2961 共点圆 CDQ分治+凸包

题目大意:给定平面,多次插入点和圆,每次插入点时询问当前插入的点是否在之前插入的所有圆中并且至少在一个圆中 直接用数据结构维护这些点和圆不是很好写,我们考虑CDQ分治 对于每层分治,我们需要对于[mid+1,r]中的每个点求出[l,mid]中是否所有的圆都覆盖了这个点 设点的坐标为(x0,y0),那么这个点在所有圆内必须满足对于所有的圆心(x,y),(x-x0)^2+(y-y0)^2<=x^2+y^2,即2*x*x0+2*y*y0>=x0^2+y0^2 我们发现上面的式子是一个半平面,斜率为-