BZOJ 2877 NOI2012 魔幻棋盘 二维线段树

题目大意:给定一个矩阵,支持两种操作:

1.将某个子矩阵中的每个值增加一个数

2.询问某个子矩阵中的所有数的GCD 已知所有询问恒过定点(x,y)

算了BZOJ没有原题我还是把原题发上来吧- -

《论代码长度与注释长度以及题目简单程度的比例失调关系以及日本饮用水资源的解决方案》

《10K+代码是怎样炼成的》

《GCD与修改标记的正确用法》

《出题人我*你吗系列》

《懒惰即美德》

咳咳。

首先我们可以维护一个二维线段树支持子矩阵修改和子矩阵查询

但是我们很快就会发现这是不正确的 因为GCD(x+k,y+k)!=k+GCD(x,y)

由于GCD与修改标记不能相容 因此我们的数据结构无法支持区间修改区间查询

注意到GCD(x1,x2,...,xn)=GCD(x1,x2-x1,x3-x2,...,xn-x_n-1)

如果是一维的数组,我们可以维护一个差分,询问[x,y]时就去线段树上查询[x+1,y]的GCD,然后与a[x]取GCD就是答案

因此我们可以维护一个差分后的矩阵 这样就可以单点修改区间查询了

我一开始的做法是对左上角进行差分

这样对三个区域分别维护三种线段树,询问时将询问拆成四块就行了

但是当我写完了8KB+的四个线段树的代码之后,我发现图中的橙色区域无论如何也避免不了区间修改区间查询。。。

于是8KB的代码。。。全废了。。。

重新看题,我们发现点(x,y)永远在要询问的区域内 那么我们可以以(x,y)为中心进行差分 这样图中的绿色和橙色区域都是固定的 询问也没有必要拆分了

图中红色方块为(x,y),我们首先横向向中心差分,然后纵向向中心差分,那么就得到了最右侧的矩阵

那么询问时由于e这个值没变,因此我们直接去线段树上找到相应子矩阵的GCD就是原矩阵相应子矩阵的GCD

现在就是修改的问题 我们可以将矩阵以(x,y)为原点建系 将矩阵划分为九个区域 然后讨论修改矩阵的四个角四个边和整个位置与这九个区域的关系

图中红色区域表示+,绿色区域表示-

举个栗子,如果一个矩阵的左上角在第一象限,就对(x1-1,y1)这个点减掉修改的值

如果一个矩阵跨越了纵轴并且下边界在原点下方,就对(x,y2+1)这个点减掉修改的值

如果一个矩阵包含原点,就对(x,y)这个点加上修改的值

等号讨论的部分欢迎参见代码 最下方长得像逻辑电路的那一坨就是

此外就是线段树第一维单点修改的时候非叶节点不能直接改 要找到两个儿子的相应位置的值并合并

终于写完了。。。我が人生に、一片の悔いなし。。。

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
using namespace std;
int m,n,q;

struct Array{
	long long memory[M];
	long long* operator [] (int x)
	{
		return &memory[(x-1)*n];
	}
}a;

long long GCD(long long x,long long y)
{
	return y?GCD(y,x%y):x;
}

inline long long Abs(long long x)
{
	return x<0?-x:x;
}

struct Segtree{
	Segtree *ls,*rs;
	long long val;
	Segtree():ls(0x0),rs(0x0),val(0ll) {}
	friend void Build_Tree(Segtree* &p,int x,int y,long long a[])
	{
		int mid=x+y>>1;
		p=new Segtree;
		if(x==y)
		{
			p->val=a[mid];
			return ;
		}
		Build_Tree(p->ls,x,mid,a);
		Build_Tree(p->rs,mid+1,y,a);
		p->val=GCD(p->ls->val,p->rs->val);
		//普通线段树
	}
	void Modify(int x,int y,int pos,long long val)
	{
		int mid=x+y>>1;
		if(x==y)
		{
			this->val+=val;
			return ;
		}
		if(pos<=mid) ls->Modify(x,mid,pos,val);
		else rs->Modify(mid+1,y,pos,val);
		this->val=GCD(ls->val,rs->val);
	}
	long long Get_Ans(int x,int y,int l,int r)
	{
		int mid=x+y>>1;
		if(x==l&&y==r)
			return val;
		if(r<=mid) return ls->Get_Ans(x,mid,l,r);
		if(l>mid) return rs->Get_Ans(mid+1,y,l,r);
		return GCD( ls->Get_Ans(x,mid,l,mid) , rs->Get_Ans(mid+1,y,mid+1,r) );
	}
	friend void Union(Segtree* &p,Segtree *p1,Segtree *p2,int x,int y)
	{
		int mid=x+y>>1;
		if(!p) p=new Segtree;
		if(x==y)
		{
			p->val=GCD(p1->val,p2->val);
			return ;
		}
		Union(p->ls,p1->ls,p2->ls,x,mid);
		Union(p->rs,p1->rs,p2->rs,mid+1,y);
		p->val=GCD(p->ls->val,p->rs->val);
	}
};

struct abcd{
	abcd *ls,*rs;
	Segtree *tree;
	abcd():ls(0x0),rs(0x0),tree(0x0) {}
	friend void Build_Tree(abcd* &p,int x,int y,Array& a)
	{
		int mid=x+y>>1;
		p=new abcd;
		if(x==y)
		{
			Build_Tree(p->tree,1,n,a[mid]);
			return ;
		}
		Build_Tree(p->ls,x,mid,a);
		Build_Tree(p->rs,mid+1,y,a);
		Union(p->tree,p->ls->tree,p->rs->tree,1,n);
	}
	void Modify(int x,int y,int pos1,int pos2,long long val)
	{
		int mid=x+y>>1;
		if(x==y)
		{
			tree->Modify(1,n,pos2,val);
			return ;
		}
		if(pos1<=mid) ls->Modify(x,mid,pos1,pos2,val);
		else rs->Modify(mid+1,y,pos1,pos2,val);
		long long lval=ls->tree->Get_Ans(1,n,pos2,pos2);
		long long rval=rs->tree->Get_Ans(1,n,pos2,pos2);
		long long this_val=tree->Get_Ans(1,n,pos2,pos2);
		tree->Modify(1,n,pos2,GCD(lval,rval)-this_val);
	}
	long long Get_Ans(int x,int y,int l1,int r1,int l2,int r2)
	{
		int mid=x+y>>1;
		if(x==l1&&y==r1)
			return tree->Get_Ans(1,n,l2,r2);
		if(r1<=mid) return ls->Get_Ans(x,mid,l1,r1,l2,r2);
		if(l1>mid) return rs->Get_Ans(mid+1,y,l1,r1,l2,r2);
		return GCD( ls->Get_Ans(x,mid,l1,mid,l2,r2) ,
					rs->Get_Ans(mid+1,y,mid+1,r1,l2,r2) );
	}
}*root;

int main()
{

	#ifndef ONLINE_JUDGE
	freopen("2877.in","r",stdin);
	freopen("2877.out","w",stdout);
	#endif

	int i,j,x,y,p,x1,y1,x2,y2;
	long long val;
	cin>>m>>n>>x>>y>>q;
	for(i=1;i<=m;i++)
		for(j=1;j<=n;j++)
			#ifdef ONLINE_JUDGE
				scanf("%lld",&a[i][j]);
			#else
				scanf("%I64d",&a[i][j]);
			#endif
	for(i=1;i<=m;i++)
	{
		for(j=1;j<y;j++)
			a[i][j]-=a[i][j+1];
		for(j=n;j>y;j--)
			a[i][j]-=a[i][j-1];
	}
	for(j=1;j<=n;j++)
	{
		for(i=1;i<x;i++)
			a[i][j]-=a[i+1][j];
		for(i=m;i>x;i--)
			a[i][j]-=a[i-1][j];
	}
	Build_Tree(root,1,m,a);
	for(i=1;i<=q;i++)
	{
		scanf("%d",&p);
		if(p==0)
		{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			x1=x-x1;y1=y-y1;x2=x+x2;y2=y+y2;
			long long ans=root->Get_Ans(1,m,x1,x2,y1,y2);
			ans=Abs(ans);
			#ifdef ONLINE_JUDGE
				printf("%lld\n",ans);
			#else
				printf("%I64d\n",ans);
			#endif
		}
		else
		{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			#ifdef ONLINE_JUDGE
				scanf("%lld",&val);
			#else
				scanf("%I64d",&val);
			#endif

			if( x1<=x && y1<=y && x1-1>0 && y1-1>0 )
				root->Modify(1,m,x1-1,y1-1,val);
			else if( x1<=x && y1>y && x1-1>0 )
				root->Modify(1,m,x1-1,y1,-val);
			else if( x1>x && y1<=y && y1-1>0 )
				root->Modify(1,m,x1,y1-1,-val);
			else if( x1>x && y1>y )
				root->Modify(1,m,x1,y1,val);
			//左上端点(x1,y1)

			if( x1<=x && y2>=y && x1-1>0 && y2+1<=n )
				root->Modify(1,m,x1-1,y2+1,val);
			else if( x1<=x && y2<y && x1-1>0 )
				root->Modify(1,m,x1-1,y2,-val);
			else if( x1>x && y2>=y && y2+1<=n )
				root->Modify(1,m,x1,y2+1,-val);
			else if( x1>x && y2<y )
				root->Modify(1,m,x1,y2,val);
			//右上端点(x1,y2)

			if( x2>=x && y1<=y && x2+1<=m && y1-1>0 )
				root->Modify(1,m,x2+1,y1-1,val);
			else if( x2<x && y1<=y && y1-1>0 )
				root->Modify(1,m,x2,y1-1,-val);
			else if( x2>=x && y1>y && x2+1<=m )
				root->Modify(1,m,x2+1,y1,-val);
			else if( x2<x && y1>y )
				root->Modify(1,m,x2,y1,val);
			//左下端点(x2,y1)

			if( x2>=x && y2>=y && x2+1<=m && y2+1<=n )
				root->Modify(1,m,x2+1,y2+1,val);
			else if( x2<x && y2>=y && y2+1<=n )
				root->Modify(1,m,x2,y2+1,-val);
			else if( x2>=x && y2<y && x2+1<=m )
				root->Modify(1,m,x2+1,y2,-val);
			else if( x2<x && y2<y )
				root->Modify(1,m,x2,y2,val);
			//右下端点(x2,y2)

			if( x1<=x && x2>=x )
			{
				if( y1<=y && y1-1>0 ) root->Modify(1,m,x,y1-1,-val);
				else if( y1>y ) root->Modify(1,m,x,y1,val);
				//左端点(x,y1)
				if( y2>=y && y2+1<=n ) root->Modify(1,m,x,y2+1,-val);
				else if( y2<y ) root->Modify(1,m,x,y2,val);
				//右端点(x,y2)
			}

			if( y1<=y && y2>=y )
			{
				if( x1<=x && x1-1>0 ) root->Modify(1,m,x1-1,y,-val);
				else if( x1>x ) root->Modify(1,m,x1,y,val);
				//上端点(x1,y)
				if( x2>=x && x2+1<=m ) root->Modify(1,m,x2+1,y,-val);
				else if( x2<x ) root->Modify(1,m,x2,y,val);
				//下端点(x2,y)
			}

			if( x1<=x && x2>=x && y1<=y && y2>=y )
				root->Modify(1,m,x,y,val);
		}
	}
	return 0;
}
时间: 2024-11-06 20:58:41

BZOJ 2877 NOI2012 魔幻棋盘 二维线段树的相关文章

BZOJ 1513 POI2006 Tet-Tetris 3D 二维线段树

题目大意:给定一个矩阵,初始每个位置上的元素都是0,每次选择一个子矩形,将这个子矩形内的值修改为这个子矩形内的最大值+h,求最终所有位置上的最大值 我们需要维护一种数据结构,支持更新子矩形的值和查询子矩形最大值 似乎二维线段树就可以了? 但是YY了一下我们会发现两个没法解决的问题: 1.标记的下传 2.信息的上传 其实... 第一个很好办嘛!不下传不就好了! 标记永久化,无需下传,只要查询的时候对线段树路径上的每一个点都询问一遍就行了! 那么第二个呢? 第二个很好办嘛!不上传不就好了! 由于修改

BZOJ 1513 POI 2006 Tet-Tetris 3D 二维线段树

题目大意:三维俄罗斯方块,问最后摞了多高. 思路:二维线段树的裸题.但是要注意二维线段树不支持标记下穿.所以就不下传,每次更新答案的时候先看标记,然后用所有的跟标记比较大小之后返回. 具体看代码吧,不知道怎么说. CODE: #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define

[BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)

4785: [Zjoi2017]树状数组 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 297  Solved: 195[Submit][Status][Discuss] Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道 基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1)

HDU1832 二维线段树求最值(模板)

Luck and Love Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 50 Accepted Submission(s): 20   Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘

ZOJ 2859 二维线段树

思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比较了解了:但是还没遇到在两个线段树中同时进行push操作的,其实这题我是想在x维和y维同时进行push操作的,但是想了好久不会,然后看到这题又给出10秒,然后想想在x维线段直接单点查询肯定也过了,然后在第二维就只有pushup操作,在第一维线段树没有pushup操作.要是在第一维也有pushup操作的话,那就不用单点查询那么慢了.不过也A了,想找题即在二维同时进行pushup和pushdown操作的. #include<iost

[POJ2155] Matrix(二维线段树,树套树)

题目链接:http://poj.org/problem?id=2155 题意:给一个01矩阵,两个操作,翻转:子矩阵里每一个数都由0变1,1变0. 查询:查询某一点是0还是1. 一直以为二维线段树就是开一个线段树数组的我- 这题暴力更新每一个小矩形,翻转就+1,最后看看某点的奇偶. 写屎了,特别注意的是在外层查询的时候要先把当前层的内层query掉,接着再向下扩展.这样外层就不用lazy啦 1 #include <algorithm> 2 #include <iostream> 3

poj1195 Mobile phones 二维线段树入门

二维线段树就是树套树,线段树套线段树... #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define lson l,m,rt<<1 #

UVA 11297 Census ——二维线段树

[题目分析] 二维线段树模板题目. 简直就是无比的暴力.时间复杂度为两个log. 标记的更新方式比较奇特,空间复杂度为N^2. 模板题目. [代码] #include <cstdio> #include <cstring> //#include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <str

tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树

P1716 - 上帝造题的七分钟 From Riatre    Normal (OI)总时限:50s    内存限制:128MB    代码长度限制:64KB 背景 Background 裸体就意味着身体. 描述 Description “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵.第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作.第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作.第