bzoj2877 [Noi2012]魔幻棋盘 二维线段树

题目大意:
维护一个矩阵支持两个操作:
0、查询子矩形gcd。
1、子矩形整体加一个值。
所有的询问都会一定包含同一个点。

题目分析:
PoPoQQQ讲解的非常好啊这个……
二维线段树可以资磁子矩形查询,但是它不资磁子矩形修改啊orz
由于gcd(a,b)=gcd(a-b,b),所以我们可以考虑差分。
但是上面这个式子使用是有前提的,就是把一些数差分之后,其中必须有一个数时原数才行,恰好所有询问都包含同一个点,那就把这个点作为中心来差分,至于如何二维差分这个问题,就是先把每一行以y为中心差分一下,再把每一列以x为中心差分一下,就行了。
然后修改从区间修改变成了单点修改。
然后分成21种情况讨论一下要修改的点是哪些就可以了。
其实这个题思路到没有多难,就是讨论起来太恶心了orz……

代码如下:

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <ctime>
#define N 520000
using namespace std;
typedef long long LL;
inline LL Abs(LL x) { return x<0?-x:x; }
LL gcd(LL a,LL b) { return b?gcd(b,a%b):a; }
int n,m,sx,sy,T;
class Array{
private:
    LL memory[N];
public:
    LL* operator [] (int c)
    {
        return &memory[(c-1)*m];
    }
}a;
struct segy{
    segy *ls,*rs;
    LL val;
    void* operator new (size_t size);
};
void* segy :: operator new (size_t size)
{
    static segy *C=NULL,*mempool=NULL;
    if(C==mempool)
    {
        mempool=(C=new segy[1<<15])+(1<<15);
        memset(C,0,sizeof(segy)*(1<<15));
    }
    return C++;
}
void build_tree(segy *&c,int l,int r,int x)
{
    c=new segy;
    if(l==r)
    {
        c->val=x?a[x][l]:0;
        return;
    }
    int mid=l+r>>1;
    build_tree(c->ls,l,mid,x);
    build_tree(c->rs,mid+1,r,x);
    c->val=x?gcd(c->ls->val,c->rs->val):0;
}
void update(segy *&c,int l,int r,int x,LL y)
{
    if(l==r)
    {
        c->val+=y;
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) update(c->ls,l,mid,x,y);
    else update(c->rs,mid+1,r,x,y);
    c->val=gcd(c->ls->val,c->rs->val);
}
LL query(segy *c,int l,int r,int x,int y)
{
    if(x<=l && y>=r) return c->val;
    int mid=l+r>>1;
    if(y<=mid) return query(c->ls,l,mid,x,y);
    if(x>mid) return query(c->rs,mid+1,r,x,y);
    return gcd(query(c->ls,l,mid,x,y),query(c->rs,mid+1,r,x,y));
}
void Merge(segy *&c,segy *x,segy *y,int l,int r)
{
    c->val=gcd(x->val,y->val);
    if(l==r) return;
    int mid=l+r>>1;
    Merge(c->ls,x->ls,y->ls,l,mid);
    Merge(c->rs,x->rs,y->rs,mid+1,r);
}
void Merge(segy *&c,segy *x,segy *y,int l,int r,int z)
{
    c->val=gcd(x->val,y->val);
    if(l==r) return;
    int mid=l+r>>1;
    if(z<=mid) Merge(c->ls,x->ls,y->ls,l,mid,z);
    else Merge(c->rs,x->rs,y->rs,mid+1,r,z);
}
struct segx{
    segx *ls,*rs;
    segy *tree;
    void* operator new (size_t size);
}*root;
void* segx :: operator new(size_t size)
{
    static segx *C=NULL,*mempool=NULL;
    if(C==mempool)
    {
        mempool=(C=new segx[1<<15])+(1<<15);
        memset(C,0,sizeof(segx)*(1<<15));
    }
    return C++;
}
void build_tree(segx *&c,int l,int r)
{
    c=new segx;
    build_tree(c->tree,1,m,l==r?l:0);
    if(l==r) return;
    int mid=l+r>>1;
    build_tree(c->ls,l,mid);
    build_tree(c->rs,mid+1,r);
    Merge(c->tree,c->ls->tree,c->rs->tree,1,m);
}
void update(segx *c,int l,int r,int x,int y,LL z)
{
    if(l==r)
    {
        update(c->tree,1,m,y,z);
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) update(c->ls,l,mid,x,y,z);
    else update(c->rs,mid+1,r,x,y,z);
    Merge(c->tree,c->ls->tree,c->rs->tree,1,m,y);
}
LL query(segx *c,int l,int r,int x1,int x2,int y1,int y2)
{
    if(x1<=l && x2>=r) return query(c->tree,1,m,y1,y2);
    int mid=l+r>>1;
    if(x2<=mid) return query(c->ls,l,mid,x1,x2,y1,y2);
    if(x1>mid) return query(c->rs,mid+1,r,x1,x2,y1,y2);
    return gcd(query(c->ls,l,mid,x1,x2,y1,y2),query(c->rs,mid+1,r,x1,x2,y1,y2));
}
inline void update(int x,int y,LL z) { if(x>=1 && x<=n && y>=1 && y<=m) update(root,1,n,x,y,z); }
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%d%d",&sx,&sy);
    scanf("%d",&T);
    int X1,X2,Y1,Y2,opt;
    LL c;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lld",&a[i][j]);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<sy;j++) a[i][j]=a[i][j]-a[i][j+1];
        for(int j=m;j>sy;j--) a[i][j]=a[i][j]-a[i][j-1];
    }
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<sx;i++) a[i][j]=a[i][j]-a[i+1][j];
        for(int i=n;i>sx;i--) a[i][j]=a[i][j]-a[i-1][j];
    }
    build_tree(root,1,n);
    for(int i=1;i<=T;i++)
    {
        scanf("%d%d%d%d%d",&opt,&X1,&Y1,&X2,&Y2);
        if(opt)
        {
            scanf("%lld",&c);
            if(X1<=sx && Y1<=sy && X2>=sx && Y2>=sy) update(sx,sy,c);
            if(Y1<=sy && Y2>=sy)
            {
                if(X1<=sx) update(X1-1,sy,-c);
                else update(X1,sy,c);
                if(X2>=sx) update(X2+1,sy,-c);
                else update(X2,sy,c);
            }
            if(X1<=sx && X2>=sx)
            {
                if(Y1<=sy) update(sx,Y1-1,-c);
                else update(sx,Y1,c);
                if(Y2>=sy) update(sx,Y2+1,-c);
                else update(sx,Y2,c);
            }
                 if(X1<=sx && Y1<=sy) update(X1-1,Y1-1,c);
            else if(X1<=sx && Y1>sy) update(X1-1,Y1,-c);
            else if(X1>sx && Y1<=sy) update(X1,Y1-1,-c);
            else update(X1,Y1,c);
                 if(X1<=sx && Y2<sy) update(X1-1,Y2,-c);
            else if(X1<=sx && Y2>=sy) update(X1-1,Y2+1,c);
            else if(X1>sx && Y2<sy) update(X1,Y2,c);
            else update(X1,Y2+1,-c);
                 if(X2<sx && Y1<=sy) update(X2,Y1-1,-c);
            else if(X2<sx && Y1>sy) update(X2,Y1,c);
            else if(X2>=sx && Y1<=sy) update(X2+1,Y1-1,c);
            else update(X2+1,Y1,-c);
                 if(X2<sx && Y2<sy) update(X2,Y2,c);
            else if(X2<sx && Y2>=sy) update(X2,Y2+1,-c);
            else if(X2>=sx && Y2<sy) update(X2+1,Y2,-c);
            else update(X2+1,Y2+1,c);
        }
        else printf("%lld\n",Abs(query(root,1,n,sx-X1,sx+X2,sy-Y1,sy+Y2)));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值