【BZOJ】2877:[Noi2012]魔幻棋盘-线段树套线段树&差分

题解

gcd(a,b)=c g c d ( a , b ) = c ,可得 c|a,c|b c | a , c | b ,那么 (ab)|c ( a − b ) | c ,由可以把 b b 全部提出来,即为(a mod b)|c,所以求 gcd g c d 可以欧几里得辗转相除。
那么对于这道题,显然是一个线段树的操作。
维护区间 gcd g c d 可以直接维护上传,维护区间加也显然可以。但两个操作一起执行就不可以了。
那么结合求 gcd g c d 可以辗转相除的性质,我们对这个矩形进行二维差分。差分后的区间 gcd g c d 不变,但区间加操作转变为四个单点加,显然单点加直接 log2n l o g 2 n 更新后 update u p d a t e 上来即可。
考虑到差分求 gcd g c d 也需要从差分的起始点求,而 gcd g c d 并不支持删去和加成。
而这道题恰恰规定了起始点(向四个方向延伸),那么就可以按四个象限的方向分别做差分,询问的时候就不需要再做任何处理了。但这样也造成了修改的麻烦。
因为是二维平面上的差分,而且四个象限中都需要讨论到,所以很麻烦。要把这个加操作区域分成四个顶点,两对对边,一个中心点来讨论(具体也可以看代码实现。

ps:题面有一个错误:

表示询问的区域是以棋盘守护者的位置为基础向上扩展x1行,向下扩展y1行,向左扩展x2列,向右扩展y2列得到的矩形区域(详见样例

这里应改为:向上扩展 x1 x 1 行,向下扩展 x2 x 2 行,向左扩展 y1 y 1 列,向右扩展 y2 y 2 列,这题面真的坑人啊。

if i f else e l s e 一定要多打括号…


代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1000,M=2e7;
typedef long long ll;
int n,m,T,tot;ll ans;

struct Array{
    ll num[N];
    ll * operator[] (int x){return &num[(x-1)*m];}
}a,b;

struct P{
  int x,y;
  P(){}
  P(int X,int Y){x=X;y=Y;}
}ori,st,ed,des;

int ch[M][2],rt[N<<2],cnt;
ll w[M];

template<class T>
inline void rd(T &x){
    x=0;char c=getchar();int f=0;
    while(!isdigit(c)){if(c=='-') f=1;c=getchar();}
    while(isdigit(c)){x=x*10+(c^48);c=getchar();}
    if(f) x=-x;
}

inline ll bs(ll x) {return x<0?-x:x;}
inline ll gcd(ll x,ll y){return y==0?bs(x):gcd(y,x%y);}

namespace tree{
   #define ls (k<<1)
   #define rs (k<<1|1)
   #define lc ch[k][0]
   #define rc ch[k][1]
   #define mid (((l)+(r))>>1)

   inline void pushup(int k){w[k]=gcd(w[lc],w[rc]);}

   inline void setup(int &k,int l,int r,int pos)
   {
      k=++cnt;
      if(l==r) {w[k]=b[pos][l];return;}
      setup(lc,l,mid,pos);setup(rc,mid+1,r,pos);
      pushup(k);
   }

   inline void merge(int &k,int ix,int iy,int l,int r)
   {
       if(!k) k=++cnt;
       w[k]=gcd(w[ix],w[iy]);
       if(l==r) return;
       merge(lc,ch[ix][0],ch[iy][0],l,mid);
       merge(rc,ch[ix][1],ch[iy][1],mid+1,r);
   }

   inline void build(int k,int l,int r)
   {
       if(l==r) {setup(rt[k],1,m,l);return;}
       build(ls,l,mid);build(rs,mid+1,r);
       merge(rt[k],rt[ls],rt[rs],1,m);
   }

   inline ll get(int k,int l,int r)
   {
       if(ed.x<=l && r<=ed.y) return w[k];
       ll re=0;
       if(ed.x<=mid) re=get(lc,l,mid);
       if(ed.y>mid) re=gcd(get(rc,mid+1,r),re);
       return re;
   }

   inline ll query(int k,int l,int r)
   {
      if(st.x<=l && r<=st.y) return get(rt[k],1,m);
      ll re=0;
      if(st.x<=mid) re=query(ls,l,mid);
      if(st.y>mid) re=gcd(query(rs,mid+1,r),re);
      return re;
   }

   inline void modify(int k,int l,int r,ll val)
   {
      if(l==r) {w[k]+=val;return;}
      if(des.y<=mid) modify(lc,l,mid,val);
      else modify(rc,mid+1,r,val);
      pushup(k);
   }

   inline void update(int k,int ix,int iy,int l,int r)
   {
      w[k]=gcd(w[ix],w[iy]);
      if(l==r) return;
      if(des.y<=mid) update(lc,ch[ix][0],ch[iy][0],l,mid);
      else update(rc,ch[ix][1],ch[iy][1],mid+1,r);
   }

   inline void ad(int k,int l,int r,ll val)
   { 
      if(l==r){modify(rt[k],1,m,val);return;}
      if(des.x<=mid) ad(ls,l,mid,val);
      else ad(rs,mid+1,r,val);
      update(rt[k],rt[ls],rt[rs],1,m);
   }
}
using namespace tree;
int main(){
    int i,j,op,ix,iy;ll iz;
    rd(n);rd(m);rd(ori.x);rd(ori.y);rd(T);
    for(i=1;i<=n;++i) 
     for(j=1;j<=m;++j)
      rd(a[i][j]);
    for(i=1;i<=n;++i)
     for(j=1;j<=m;++j){
        b[i][j]=a[i][j];
        if(i<ori.x) b[i][j]-=a[i+1][j];
        if(i>ori.x) b[i][j]-=a[i-1][j];
        if(j<ori.y) b[i][j]-=a[i][j+1];
        if(j>ori.y) b[i][j]-=a[i][j-1];
        if(i==ori.x || j==ori.y) continue;
        b[i][j]+=a[i+(i>ori.x?-1:1)][j+(j>ori.y?-1:1)];
     }
    build(1,1,n);
    while(T--){
        rd(op);
        if(!op){
           rd(st.x);rd(ed.x);rd(st.y);rd(ed.y);
           st.x=ori.x-st.x;st.y=ori.x+st.y;
           ed.x=ori.y-ed.x;ed.y=ori.y+ed.y;
          printf("%lld\n",query(1,1,n));
        }else{
            tot++;
           rd(st.x);rd(st.y);rd(ed.x);rd(ed.y);rd(iz);

           ix=st.x;iy=st.y;
           if(ix<=ori.x){
              if(iy<=ori.y && iy>1 && ix>1)
                 {des=P(ix-1,iy-1);ad(1,1,n,iz);}
              else if(ix>1 && ori.y<iy)
                 {des=P(ix-1,iy);ad(1,1,n,-iz);}
           }else{
              if(iy<=ori.y && iy>1)
                 {des=P(ix,iy-1);ad(1,1,n,-iz);}
              else if(iy>ori.y)
                 {des=P(ix,iy);ad(1,1,n,iz);}
           }

           ix=st.x;iy=ed.y;
           if(ix<=ori.x){
              if(iy<ori.y && ix>1)
                 {des=P(ix-1,iy);ad(1,1,n,-iz);}
              else if(iy>=ori.y && ix>1 && iy<m)
                 {des=P(ix-1,iy+1);ad(1,1,n,iz);}
           }else{
              if(iy<ori.y)
                 {des=P(ix,iy);ad(1,1,n,iz);}
              else if(iy>=ori.y && iy<m)
              {des=P(ix,iy+1);ad(1,1,n,-iz);}
           }

           ix=ed.x;iy=ed.y;
           if(ix<ori.x){
             if(iy<ori.y)
                {des=P(ix,iy);ad(1,1,n,iz);}
             else if(iy>=ori.y && iy<m)
             {des=P(ix,iy+1);ad(1,1,n,-iz);}
           }else{
             if(iy<ori.y && ix<n)
             {des=P(ix+1,iy);ad(1,1,n,-iz);}
             else if(iy>=ori.y && ix<n && iy<m)
             {des=P(ix+1,iy+1);ad(1,1,n,iz);}
           }

           ix=ed.x;iy=st.y;
           if(ix<ori.x){
             if(iy<=ori.y && iy>1)
                {des=P(ix,iy-1);ad(1,1,n,-iz);}
             else if(iy>ori.y)
             {des=P(ix,iy);ad(1,1,n,iz);}
           }else{
             if(iy<=ori.y && ix<n && iy>1)
                {des=P(ix+1,iy-1);ad(1,1,n,iz);}
             else if(iy>ori.y && ix<n)
             {des=P(ix+1,iy);ad(1,1,n,-iz);}
           }

           if(st.x<=ori.x && ed.x>=ori.x){
              if(st.y>ori.y)
              {des=P(ori.x,st.y);ad(1,1,n,iz);}
              else if(st.y>1)
                 {des=P(ori.x,st.y-1);ad(1,1,n,-iz);}
              if(ed.y<ori.y)
                 {des=P(ori.x,ed.y);ad(1,1,n,iz);}
              else if(ed.y<m)
                 {des=P(ori.x,ed.y+1);ad(1,1,n,-iz);}
           }

           if(st.y<=ori.y && ed.y>=ori.y){
              if(st.x>ori.x)
                 {des=P(st.x,ori.y);ad(1,1,n,iz);}
              else if(st.x>1)
                 {des=P(st.x-1,ori.y);ad(1,1,n,-iz);}
              if(ed.x<ori.x)
                 {des=P(ed.x,ori.y);ad(1,1,n,iz);}
              else if(ed.x<n)
                 {des=P(ed.x+1,ori.y);ad(1,1,n,-iz);}
           }

           if(st.x<=ori.x && ed.x>=ori.x && st.y<=ori.y && ed.y>=ori.y)
           {des=ori;ad(1,1,n,iz);}

        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值