bzoj2877: [Noi2012]魔幻棋盘 树套数+差分

神题,只能膜拜题解。然后发现了差分这个东西,所谓差分就是sigma{a[i]}=b[i]; a[i]=b[i]-b[i-1];(是不是和辗转相减很像。。。。。)

我们再来看一下二维的怎么做。。然后我们换一种维护的方法。因为题目要求一定会查询到守护者a[x, y],所以我们以(x, y)为原点,建立直角座标系。二维的一样是得差分,对于第四象限的点:b[i, j] = a[i, j] - a[i-1, j] - a[i, j-1] + a[i-1, j-1],其他三个象限类似。

所以对于定点(X,Y) 我们只需要维护其所在的那一行,和那一列的一维差分,和其它四个部分的二维差分。

区间更新的话,也只需要更新四个顶点就好了,因为是差分。然后二维线段去暴搞。

#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define getmid int mid=(l+r)>>1
#define maxn 555555
typedef long long sint;
int getint()
{
    int res,f=1;char c;
    while(c=getchar(),c<'0'||c>'9') {if(c=='-') f=-1;}
    res=c-'0';
    while(c=getchar(),c>='0'&&c<='9') res=res*10+c-'0';
    return res*f;
}
sint getsint()
{
    sint res,f=1;char c;
    while(c=getchar(),c<'0'||c>'9') {if(c=='-') f=-1;}
    res=c-'0';
    while(c=getchar(),c>='0'&&c<='9') res=res*10+c-'0';
    return res*f;
}
int n,m,x,y,t,x1,x2,y1,y2,opt;
sint d[maxn<<2],a[maxn<<2],s[2][maxn<<2],d1[maxn],d2[maxn],g[maxn<<4],c,ans;
sint gcd(sint a,sint b)
{
    if(a<0) a=-a;if(b<0) b=-b;
    if(a<b) swap(a,b);
    if(b==0) return a;
    return gcd(b,a%b);
}
int pos(int a,int b)
{
    return (a-1)*m+b;
}
int pos2(int a,int b)
{
    return (a-1)*(m*4+100)+b;
}
void upy(int rtx,int flag,int l,int r,int rt,int pos,sint v)
{
    if(l==r)
    {
        if(flag)
        {
            g[pos2(rtx,rt)]=v;
        }
        else
        {
            g[pos2(rtx,rt)]=gcd(g[pos2(rtx<<1,rt)],g[pos2(rtx<<1|1,rt)]);
        }
        return;
    }
    getmid;
    if(pos<=mid) upy(rtx,flag,lson,pos,v);
    else upy(rtx,flag,rson,pos,v);
    g[pos2(rtx,rt)]=gcd(g[pos2(rtx,rt<<1)],g[pos2(rtx,rt<<1|1)]);
}
void upx(int l,int r,int rt,int xx,int yy,sint v)
{
    if(l==r)
    {
        upy(rt,1,1,m,1,yy,v);
        return;
    }
    getmid;
    if(xx<=mid) upx(lson,xx,yy,v);
    else upx(rson,xx,yy,v);
    upy(rt,0,1,m,1,yy,v);
}
void update(int flag,int l,int r,int rt,int pos,sint v)
{
    if(l==r)
    {
        s[flag][rt]=v;
        return;
    }
    getmid;
    if(pos<=mid) update(flag,lson,pos,v);
    else update(flag,rson,pos,v);
    s[flag][rt]=gcd(s[flag][rt<<1],s[flag][rt<<1|1]);
}
sint query(int flag,int l,int r,int rt,int xx,int yy)
{
    if(xx>yy) return 0;
    if(xx<=l&&r<=yy)
    {
        return s[flag][rt];
    }
    getmid;
    sint a=0,b=0;
    if(xx<=mid) a=query(flag,lson,xx,yy);
    if(yy>mid) b=query(flag,rson,xx,yy);
    return gcd(a,b);
}
sint quy(int rtx,int l,int r,int rt,int xx,int yy)
{
    if(xx<=l&&r<=yy)
    {
        return g[pos2(rtx,rt)];
    }
    getmid;
    sint a=0,b=0;
    if(xx<=mid) a=quy(rtx,lson,xx,yy);
    if(yy>mid) b=quy(rtx,rson,xx,yy);
    return gcd(a,b);
}
sint qux(int l,int r,int rt,int xx,int yy,int xx1,int yy1)
{
    if (xx1>yy1||xx>yy) return 0;
    if(xx<=l&&r<=yy)
    {
        return quy(rt,1,m,1,xx1,yy1);
    }
    getmid;
    sint a=0,b=0;
    if(xx<=mid) a=qux(lson,xx,yy,xx1,yy1);
    if(yy>mid) b=qux(rson,xx,yy,xx1,yy1);
    return gcd(a,b);
}
int main()
{
    //freopen("chess1.in","r",stdin);
    scanf("%d%d%d%d%d",&n,&m,&x,&y,&t);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            d[pos(i,j)]=getsint();
    for(int i=1;i<n;i++)
    {
        for(int j=1;j<m;j++)
        {
            a[pos(i,j)]=d[pos(i,j)]-d[pos(i+1,j)]-d[pos(i,j+1)]+d[pos(i+1,j+1)];
            upx(1,n,1,i,j,a[pos(i,j)]);
        }
    }
    for(int i=1;i<n;i++)
    {
        d1[i]=d[pos(i+1,y)]-d[pos(i,y)];
        update(0,1,n,1,i,d1[i]);
    }
    for(int i=1;i<m;i++)
    {
        d2[i]=d[pos(x,i+1)]-d[pos(x,i)];
        update(1,1,m,1,i,d2[i]);
    }
    while(t--)
    {
        opt=getint();
        x1=getint();y1=getint();
        x2=getint();y2=getint();
        if(opt==0)
        {
            ans=gcd(d[pos(x,y)],qux(1,n,1,x-x1,x+x2-1,y-y1,y+y2-1));
            ans=gcd(ans,query(0,1,n,1,x-x1,x+x2-1));
            ans=gcd(ans,query(1,1,m,1,y-y1,y+y2-1));
            printf("%lld\n",ans);
        }
        else
        {
            c=getsint();
            if(x1!=1&&y1!=1) upx(1,n,1,x1-1,y1-1,a[pos(x1-1,y1-1)]+=c);
            if(x1!=1&&y2!=m) upx(1,n,1,x1-1,y2,a[pos(x1-1,y2)]-=c);
            if(x2!=n&&y1!=1) upx(1,n,1,x2,y1-1,a[pos(x2,y1-1)]-=c);
            if(x2!=n&&y2!=m) upx(1,n,1,x2,y2,a[pos(x2,y2)]+=c);
            if (y1<=y&&y2>=y)
            {
                if (x1!=1) update(0,1,n,1,x1-1,d1[x1-1]+=c);
                if (x2!=n) update(0,1,n,1,x2,d1[x2]-=c);
            }
            if (x1<=x&&x2>=x)
            {
                if (y1!=1) update(1,1,m,1,y1-1,d2[y1-1]+=c);
                if (y2!=m) update(1,1,m,1,y2,d2[y2]-=c);
            }
            if(x1<=x&&x<=x2&&y1<=y&&y<=y2)
            {
                d[pos(x,y)]+=c;
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值