【二维线段树(二维区间GCD)】[NOI2012]魔幻棋盘

题目描述

<h2>题目描述</h2><div class=content><div>将要读二年级的小Q买了一款新型益智玩具——魔幻棋盘,它是一个N行M列的网格棋盘,每个格子中均有一个正整数。棋盘守护者在棋盘的第X行第Y列(行与列均从 1 开始编号)并且始终不会移动。棋盘守护者会进行两种操作: </div><div>(a)询问:他会以自己所在位置为基础,向四周随机扩展出一块大小不定的矩形区域,向你询问这一区域内所有数的最大公约数是多少。 </div><div>(b)修改:他会随意挑选棋盘上的一块矩形区域,将这一区域内的所有数同时加上一个给定的整数。 </div><div>游戏说明书上附有这样一句话“聪明的小朋友,当你连续答对 19930324 次询问后会得到一个惊喜噢!”。小Q十分想得到这个惊喜,于是每天都在玩这个玩具。但由于他粗心大意,经常算错数,难以达到这个目标。于是他来向你寻求帮助,希望你帮他写一个程序来回答棋盘守护者的询问,并保证 100% 的正确率。 为了简化问题,你的程序只需要完成棋盘守护者的T次操作,并且问题保证任何时刻棋盘上的数字均为不超过 2^62-1 的正整数。 </div></div>
<h2>输入</h2><div class=content><div> 第一行为两个正整数N, M,表示棋盘的大小。 </div><div>第二行为两个正整数 X,Y,表示棋盘守护者的位置。 </div><div>第三行仅有一个正整数T,表示棋盘守护者将进行T次操作。 </div><div>接下来N行,每行有M个正整数,用来描述初始时棋盘上每个位置的数。 </div><div>接下来T行,按操作的时间顺序给出T次操作。</div><div>每行描述一次操作,以一个数字0或1开头: </div><div>若以数字0开头,表示此操作为询问,随后会有四个非负整数 X1,Y1,X2,Y2表示询问的区域是以棋盘守护者的位置为基础向上扩展X1行,向下扩展 X</div><div>2行,向左扩展 Y1列,向右扩展 Y2列得到的矩形区域(详见样例)。 </div><div>若以数字1开头,表示此操作为修改,随后会有四个正整数 X1,Y1,X2,Y2和一个整数C,表示修改区域的上、下边界分别为第X1,X2行,左,右边界分别为第y1,y2列(详见样例),在此矩形区域内的所有数统一加上 c(注意 c 可能为负数)。</div><div></div></div><h2>输出</h2><div class=content><div> 对于每次询问操作,每行输出一个数,表示该区域内所有数的最大公约数。 </div>

分析

这是经典的区间gcd(最大公约数)问题。

差分GCD

gcd(a,b)gcd(a,b,c)=gcd(a,bka)=gcd(gcd(a,b),c)=gcd(gcd(a,ba),c)=gcd(gcd(a,ba),ck(gcd(a,ba)))=gcd(gcd(a,ba),cb)

所以区间[l,r]的最大公约数等于其差分和第一个数的最大公约数,假设a为原数组,di=aiai1,所以
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,ba),ca),dk(gcd(gcd(a,ba),ca)))=gcd(gcd(gcd(a,ba),ca),dbc+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();
}
posted @ 2016-07-09 13:35  outer_form  阅读(366)  评论(0编辑  收藏  举报