【二维线段树(二维区间GCD)】[NOI2012]魔幻棋盘
2016-07-09 13:35
363 查看
题目描述
将要读二年级的小Q买了一款新型益智玩具——魔幻棋盘,它是一个N行M列的网格棋盘,每个格子中均有一个正整数。棋盘守护者在棋盘的第X行第Y列(行与列均从 1 开始编号)并且始终不会移动。棋盘守护者会进行两种操作: (a)询问:他会以自己所在位置为基础,向四周随机扩展出一块大小不定的矩形区域,向你询问这一区域内所有数的最大公约数是多少。 (b)修改:他会随意挑选棋盘上的一块矩形区域,将这一区域内的所有数同时加上一个给定的整数。 游戏说明书上附有这样一句话“聪明的小朋友,当你连续答对 19930324 次询问后会得到一个惊喜噢!”。小Q十分想得到这个惊喜,于是每天都在玩这个玩具。但由于他粗心大意,经常算错数,难以达到这个目标。于是他来向你寻求帮助,希望你帮他写一个程序来回答棋盘守护者的询问,并保证 100% 的正确率。 为了简化问题,你的程序只需要完成棋盘守护者的T次操作,并且问题保证任何时刻棋盘上的数字均为不超过 2^62-1 的正整数。 " title="">
第一行为两个正整数N, M,表示棋盘的大小。 第二行为两个正整数 X,Y,表示棋盘守护者的位置。 第三行仅有一个正整数T,表示棋盘守护者将进行T次操作。 接下来N行,每行有M个正整数,用来描述初始时棋盘上每个位置的数。 接下来T行,按操作的时间顺序给出T次操作。每行描述一次操作,以一个数字0或1开头: 若以数字0开头,表示此操作为询问,随后会有四个非负整数 X1,Y1,X2,Y2表示询问的区域是以棋盘守护者的位置为基础向上扩展X1行,向下扩展 X2行,向左扩展 Y1列,向右扩展 Y2列得到的矩形区域(详见样例)。 若以数字1开头,表示此操作为修改,随后会有四个正整数 X1,Y1,X2,Y2和一个整数C,表示修改区域的上、下边界分别为第X1,X2行,左,右边界分别为第y1,y2列(详见样例),在此矩形区域内的所有数统一加上 c(注意 c 可能为负数)。
输出
对于每次询问操作,每行输出一个数,表示该区域内所有数的最大公约数。 " title="">分析
这是经典的区间gcd(最大公约数)问题。差分GCD
gcd(a,b)gcd(a,b,c)=gcd(a,b−ka)=gcd(gcd(a,b),c)=gcd(gcd(a,b−a),c)=gcd(gcd(a,b−a),c−k(gcd(a,b−a)))=gcd(gcd(a,b−a),c−b)所以区间[l,r]的最大公约数等于其差分和第一个数的最大公约数,假设a为原数组,di=ai−ai−1,所以
gcd(al,al+1,al+2,…,ar)=gcd(gcd(dl+1,dl+2,…,dr),al)
一维区间GCD
维护区间信息首选线段树,但是区间gcd显然没有办法打懒标记,因为下传的时候显然不知道如何修改gcd信息,所以不能有区间修改。但是原数组的区间修改对应差分数组的单点修改,所以我们用一棵线段树维护差分数组区间gcd,然后再用一个数据结构(线段树、差分数组的树状数组……)维护原数组即可。二维区间GCD
我们沿着一维区间gcd继续思考。假设a,b,c,d的相对位置是acbd
gcd(a,b,c,d)=gcd(gcd(gcd(a,b),c),d)=gcd(gcd(gcd(a,b−a),c−a),d−k(gcd(gcd(a,b−a),c−a)))=gcd(gcd(gcd(a,b−a),c−a),d−b−c+a)
二维区间gcd也满足差分的性质。
现在,我们有一个想法,是不是用二维线段树维护一个从左上角到右下角的差分数组gcd就可以了呢?
我们仔细想一下这个应该怎么做。
每次,我们要求一个二维区间的gcd,假设下图是要求的区间。
显然,白色部分是原数组的一个元素的值,很好维护,红色部分是一个二维差分,每次单点修改即可,但是绿色部分是一个一维差分,要维护的话就必须区间修改(修改一个区间内的一维线段树)。显然,二维线段树不能区间修改,所以这样不可行。
我们发现,有一个点是固定的的,我们就可以这个点为中心维护差分数组。
我们只需要维护八棵独立的线段树(四棵二维,四棵一维)就可以做到每次修改都是单点修改了。
代码
略长#include<cstdio> #include<algorithm> #include<cstring> #define MAXN 500000 using namespace std; typedef long long LL; int n,m,x,y,T; LL a[MAXN+10]; inline int Get_id(int i,int j){ return (i-1)*m+j; } template<class T> void Read(T &x){ static char c; bool f(0); while(c=getchar(),c!=EOF){ if(c=='-') f=1; else if(c>='0'&&c<='9'){ x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; ungetc(c,stdin); if(f) x=-x; return; } } } LL gcd(LL a,LL b){ LL t; while(b){ t=a%b; a=b; b=t; } return a; } namespace SegmentTree1D{ struct node{ LL d; node *ch[2]; }tree[MAXN*20+10],*tcnt=tree,*root[4]; inline void update(node *p){ p->d=gcd(p->ch[0]->d,p->ch[1]->d); } void build(node *&p,int l,int r,int x){ p=++tcnt; if(l==r){ p->d=a[Get_id(x,l)]; return; } int mid((l+r)>>1); build(p->ch[0],l,mid,x); build(p->ch[1],mid+1,r,x); update(p); } void build_col(node *&p,int l,int r){ p=++tcnt; if(l==r){ p->d=a[Get_id(l,y)]; return; } int mid((l+r)>>1); build_col(p->ch[0],l,mid); build_col(p->ch[1],mid+1,r); update(p); } void build_notleaf(node *&p,node *x,node *y,int l,int r){ p=++tcnt; if(l==r){ p->d=gcd(x->d,y->d); return; } int mid((l+r)>>1); build_notleaf(p->ch[0],x->ch[0],y->ch[0],l,mid); build_notleaf(p->ch[1],x->ch[1],y->ch[1],mid+1,r); update(p); } void insert(node *p,int l,int r,int pos,LL d){ if(l==r){ p->d+=d; return; } int mid((l+r)>>1); if(pos<=mid) insert(p->ch[0],l,mid,pos,d); else insert(p->ch[1],mid+1,r,pos,d); update(p); } void insert_notleaf(node *p,int l,int r,int pos,LL val){ if(l==r){ p->d=val; return; } int mid((l+r)>>1); if(pos<=mid) insert_notleaf(p->ch[0],l,mid,pos,val); else insert_notleaf(p->ch[1],mid+1,r,pos,val); update(p); } LL get_gcd(node *p,int l,int r,int ll,int rr){ if(ll<=l&&r<=rr) return p->d; if(ll>r||rr<l) return 0; int mid((l+r)>>1); return gcd(get_gcd(p->ch[0],l,mid,ll,rr),get_gcd(p->ch[1],mid+1,r,ll,rr)); } } namespace SegmentTree2D{ using namespace SegmentTree1D; struct node{ SegmentTree1D::node *root; node *ch[2]; }tree[MAXN*4+10],*tcnt=tree,*root[4]; inline void update(node *p,int al,int ar,int pos){ insert_notleaf(p->root,al,ar,pos,gcd(get_gcd(p->ch[0]->root,al,ar,pos,pos),get_gcd(p->ch[1]->root,al,ar,pos,pos))); } inline void build_merge(node *p,int al,int ar){ build_notleaf(p->root,p->ch[0]->root,p->ch[1]->root,al,ar); } void build(node *&p,int l,int r,int al,int ar){ p=++tcnt; if(l==r){ build(p->root,al,ar,l); return; } int mid((l+r)>>1); build(p->ch[0],l,mid,al,ar); build(p->ch[1],mid+1,r,al,ar); build_merge(p,al,ar); } void insert(node *p,int l,int r,int al,int ar,int x,int y,LL d){ if(l==r){ insert(p->root,al,ar,y,d); return; } int mid((l+r)>>1); if(x<=mid) insert(p->ch[0],l,mid,al,ar,x,y,d); else insert(p->ch[1],mid+1,r,al,ar,x,y,d); update(p,al,ar,y); } LL get_gcd(node *p,int l,int r,int al,int ar,int lx,int rx,int ly,int ry){ if(lx<=l&&r<=rx) return get_gcd(p->root,al,ar,ly,ry); if(lx>r||rx<l) return 0; int mid((l+r)>>1); return gcd(get_gcd(p->ch[0],l,mid,al,ar,lx,rx,ly,ry),get_gcd(p->ch[1],mid+1,r,al,ar,lx,rx,ly,ry)); } } void read(){ Read(n),Read(m),Read(x),Read(y),Read(T); int i,t=n*m,j; for(i=1;i<=t;i++) Read(a[i]); //左 if(y>1){ for(j=1;j<y-1;j++) a[Get_id(x,j)]-=a[Get_id(x,j+1)]; SegmentTree1D::build(SegmentTree1D::root[0],1,y-1,x); } //右 if(y<m){ for(j=m;j>y+1;j--) a[Get_id(x,j)]-=a[Get_id(x,j-1)]; SegmentTree1D::build(SegmentTree1D::root[1],y+1,m,x); } //上 if(x>1){ for(i=1;i<x-1;i++) a[Get_id(i,y)]-=a[Get_id(i+1,y)]; SegmentTree1D::build_col(SegmentTree1D::root[2],1,x-1); } //下 if(x<n){ for(i=n;i>x+1;i--) a[Get_id(i,y)]-=a[Get_id(i-1,y)]; SegmentTree1D::build_col(SegmentTree1D::root[3],x+1,n); } //左上 if(x>1&&y>1){ for(i=1;i<x-1;i++){ for(j=1;j<y-1;j++) a[Get_id(i,j)]-=a[Get_id(i+1,j)]+a[Get_id(i,j+1)]-a[Get_id(i+1,j+1)]; a[Get_id(i,y-1)]-=a[Get_id(i+1,y-1)]; } for(j=1;j<y-1;j++) a[Get_id(x-1,j)]-=a[Get_id(x-1,j+1)]; SegmentTree2D::build(SegmentTree2D::root[0],1,x-1,1,y-1); } //右上 if(x>1&&y<m){ for(i=1;i<x-1;i++){ for(j=m;j>y+1;j--) a[Get_id(i,j)]-=a[Get_id(i+1,j)]+a[Get_id(i,j-1)]-a[Get_id(i+1,j-1)]; a[Get_id(i,y+1)]-=a[Get_id(i+1,y+1)]; } for(j=m;j>y+1;j--) a[Get_id(x-1,j)]-=a[Get_id(x-1,j-1)]; SegmentTree2D::build(SegmentTree2D::root[1],1,x-1,y+1,m); } //左下 if(x<n&&y>1){ for(i=n;i>x+1;i--){ for(j=1;j<y-1;j++) a[Get_id(i,j)]-=a[Get_id(i-1,j)]+a[Get_id(i,j+1)]-a[Get_id(i-1,j+1)]; a[Get_id(i,y-1)]-=a[Get_id(i-1,y-1)]; } for(j=1;j<y-1;j++) a[Get_id(x+1,j)]-=a[Get_id(x+1,j+1)]; SegmentTree2D::build(SegmentTree2D::root[2],x+1,n,1,y-1); } //右下 if(x<n&&y<m){ for(i=n;i>x+1;i--){ for(j=m;j>y+1;j--) a[Get_id(i,j)]-=a[Get_id(i-1,j)]+a[Get_id(i,j-1)]-a[Get_id(i-1,j-1)]; a[Get_id(i,y+1)]-=a[Get_id(i-1,y+1)]; } for(j=m;j>y+1;j--) a[Get_id(x+1,j)]-=a[Get_id(x+1,j-1)]; SegmentTree2D::build(SegmentTree2D::root[3],x+1,n,y+1,m); } } void solve(){ int p,x1,y1,x2,y2,tx1,ty1,tx2,ty2; LL ans,d; while(T--){ Read(p),Read(x1),Read(y1),Read(x2),Read(y2); if(p){ Read(d); if(x1<=x&&x2>=x&&y1<=y&&y2>=y) a[Get_id(x,y)]+=d; //左上 if(x1<x&&y1<y){ tx2=min(x-1,x2),ty2=min(y-1,y2); SegmentTree2D::insert(SegmentTree2D::root[0],1,x-1,1,y-1,tx2,ty2,d); if(x1>1&&y1>1) SegmentTree2D::insert(SegmentTree2D::root[0],1,x-1,1,y-1,x1-1,y1-1,d); if(x1>1) SegmentTree2D::insert(SegmentTree2D::root[0],1,x-1,1,y-1,x1-1,ty2,-d); if(y1>1) SegmentTree2D::insert(SegmentTree2D::root[0],1,x-1,1,y-1,tx2,y1-1,-d); } //右上 if(x1<x&&y2>y){ tx2=min(x-1,x2),ty1=max(y+1,y1); SegmentTree2D::insert(SegmentTree2D::root[1],1,x-1,y+1,m,tx2,ty1,d); if(x1>1&&y2<m) SegmentTree2D::insert(SegmentTree2D::root[1],1,x-1,y+1,m,x1-1,y2+1,d); if(x1>1) SegmentTree2D::insert(SegmentTree2D::root[1],1,x-1,y+1,m,x1-1,ty1,-d); if(y2<m) SegmentTree2D::insert(SegmentTree2D::root[1],1,x-1,y+1,m,tx2,y2+1,-d); } //左下 if(x2>x&&y1<y){ tx1=max(x1,x+1),ty2=min(y-1,y2); SegmentTree2D::insert(SegmentTree2D::root[2],x+1,n,1,y-1,tx1,ty2,d); if(x2<n&&y1>1) SegmentTree2D::insert(SegmentTree2D::root[2],x+1,n,1,y-1,x2+1,y1-1,d); if(x2<n) SegmentTree2D::insert(SegmentTree2D::root[2],x+1,n,1,y-1,x2+1,ty2,-d); if(y1>1) SegmentTree2D::insert(SegmentTree2D::root[2],x+1,n,1,y-1,tx1,y1-1,-d); } //右下 if(x2>x&&y2>y){ tx1=max(x1,x+1),ty1=max(y1,y+1); SegmentTree2D::insert(SegmentTree2D::root[3],x+1,n,y+1,m,tx1,ty1,d); if(x2<n&&y2<m) SegmentTree2D::insert(SegmentTree2D::root[3],x+1,n,y+1,m,x2+1,y2+1,d); if(x2<n) SegmentTree2D::insert(SegmentTree2D::root[3],x+1,n,y+1,m,x2+1,ty1,-d); if(y2<m) SegmentTree2D::insert(SegmentTree2D::root[3],x+1,n,y+1,m,tx1,y2+1,-d); } if(x1<=x&&x2>=x){ //左 if(y1<y){ ty2=min(y2,y-1); SegmentTree1D::insert(SegmentTree1D::root[0],1,y-1,ty2,d); if(y1>1) SegmentTree1D::insert(SegmentTree1D::root[0],1,y-1,y1-1,-d); } //右 if(y2>y){ ty1=max(y1,y+1); SegmentTree1D::insert(SegmentTree1D::root[1],y+1,m,ty1,d); if(y2<m) SegmentTree1D::insert(SegmentTree1D::root[1],y+1,m,y2+1,-d); } } if(y1<=y&&y2>=y){ //上 if(x1<x){ tx2=min(x2,x-1); SegmentTree1D::insert(SegmentTree1D::root[2],1,x-1,tx2,d); if(x1>1) SegmentTree1D::insert(SegmentTree1D::root[2],1,x-1,x1-1,-d); } //下 if(x2>x){ tx1=max(x+1,x1); SegmentTree1D::insert(SegmentTree1D::root[3],x+1,n,tx1,d); if(x2<n) SegmentTree1D::insert(SegmentTree1D::root[3],x+1,n,x2+1,-d); } } } else{ ans=a[Get_id(x,y)]; if(x>1&&y>1) ans=gcd(SegmentTree2D::get_gcd(SegmentTree2D::root[0],1,x-1,1,y-1,x-x1,x+x2,y-y1,y+y2),ans); if(x>1&&y<m) ans=gcd(SegmentTree2D::get_gcd(SegmentTree2D::root[1],1,x-1,y+1,m,x-x1,x+x2,y-y1,y+y2),ans); if(x<n&&y>1) ans=gcd(SegmentTree2D::get_gcd(SegmentTree2D::root[2],x+1,n,1,y-1,x-x1,x+x2,y-y1,y+y2),ans); if(x<n&&y<m) ans=gcd(SegmentTree2D::get_gcd(SegmentTree2D::root[3],x+1,n,y+1,m,x-x1,x+x2,y-y1,y+y2),ans); if(y>1) ans=gcd(SegmentTree1D::get_gcd(SegmentTree1D::root[0],1,y-1,y-y1,y+y2),ans); if(y<m) ans=gcd(SegmentTree1D::get_gcd(SegmentTree1D::root[1],y+1,m,y-y1,y+y2),ans); if(x>1) ans=gcd(SegmentTree1D::get_gcd(SegmentTree1D::root[2],1,x-1,x-x1,x+x2),ans); if(x<n) ans=gcd(SegmentTree1D::get_gcd(SegmentTree1D::root[3],x+1,n,x-x1,x+x2),ans); printf("%lld\n",abs(ans)); } } } int main() { read(); solve(); }
相关文章推荐
- OpenCV自学笔记28. Canny 源码解析
- Express框架学习总结
- Mysql Binlog三种格式介绍及分析
- 遇到别人留下的storyboard的,你需要一个引导图,但是不知道怎么跳转.
- php示例代码之 使用PHP的MySQL标准函数
- 通过ObjectAnimator动画显示数字
- Node.js 文件夹目录结构创建
- matlab实现hog特征
- java基础知识回顾---单例模式的三种实现
- 第七次CCF-D:游戏
- Shader例子(1) 高光效果(surf/顶点)
- js数组去重
- 进入本网页后自动执行本方法
- CSS中display:block属性的作用
- 安卓手册 第四章(MVP介绍和实战)
- leetcode: Missing Number
- CentOS 6.3下PostgreSQL 的安装与配置
- cuda内存处理及stream内存处理
- php示例代码之类似于C#中的String.Format方法
- ucos-II之就绪表解读