【二維線段樹(二維區間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();
}
發佈了171 篇原創文章 · 獲贊 64 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章