「NOI2012」魔幻棋盘
柒葉灬
2018-12-28 08:36:22
# 一篇题解
-----
看到这题,(这是什么鬼题!!!区间相减??还要维护$gcd$???)就知道我是不可能在考场上写出来的,因为我甚至连最基本的**二维线段树**都不会。
**前置技能**:二维线段树,动态开点线段树
$n=1$的 $ 40 $% 的情况时,给出结论:
$$ gcd(a_1,a_2,a_3,a_4)=gcd(a_1,a_2-a_1,a_3-a_2,a_4-a_3) $$
所以我们只要知道其中的一个$a_1$就行了,因为每次询问一定会有给出的位置$x \ ,\ y$,所以$y$左边的数变为$a_{x}-a_{x+1}$,右边的则是$a_{x}-a_{x-1}$。
这样的好处是什么,这样子修改的时候就可以发现**不是区间修改而是单点修改了**。
--------
转到$100$分的情况,我们可以先横着减一下变成:
$$ \begin{bmatrix} (a_{1,1}-a_{1,2})&(a_{1,2}-a_{1,3})&a_{1,3} \\(a_{2,1}-a_{2,2})&(a_{2,2}-a_{2,3})&a_{2,3}\\(a_{3,1}-a_{3,2})&(a_{3,2}-a_{3,3})&a_{3,3}\end{bmatrix}$$
竖着再减一波:
$$\begin{bmatrix} (a_{1,1}-a_{1,2}-a_{2,1}+a_{2,2} )&(a_{1,2}-a_{1,3}-a_{2,2}+a_{2,3})&(a_{1,3}-a_{2,3})\\(a_{2,1}-a_{2,2}-a_{3,1}+a_{3,2} )&(a_{2,2}-a_{2,3}-a_{3,2}+a_{3,3})&(a_{2,3}-a_{3,3})\\(a_{3,1}-a_{3,2})&(a_{3,2}-a_{3,3})&a_{3,3}\end{bmatrix}$$
得到了式子(只是左上角),发现这样子如果区间修改的话,实际上只是改$4$个地方的值即可,然后按规律找到$8$个不同地区的式子,分类讨论就行了。
```cpp
#include<bits/stdc++.h>
#define debug(x) cerr<<"\tDEBUG: "<<#x<<" = "<<(x)<<endl
using namespace std;
int n,m,x,y,q;
long long gcd(long long a,long long b){
return !b?a:gcd(b,a%b);
}
struct P20{
static const int maxn=105;
long long mp[maxn][maxn];
void solve(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&mp[i][j]);
while(q--){
int opt,x1,y1,x2,y2;
scanf("%d%d%d%d%d",&opt,&x1,&y1,&x2,&y2);
if(opt==0){
x1=max(1,x-x1);
x2=min(n,x+x2);
y1=max(1,y-y1);
y2=min(m,y+y2);
long long ans=mp[x1][y1];
for(int i=x1;i<=x2;i++)
for(int j=y1;j<=y2;j++){
ans=gcd(ans,mp[i][j]);
}
printf("%lld\n",ans);
}else {
long long c;
scanf("%lld",&c);
for(int i=x1;i<=x2;i++)
for(int j=y1;j<=y2;j++)
mp[i][j]+=c;
}
}
}
}p20;
struct P60{
static const int maxn=5e5+5;
long long A[maxn];
struct node{
int l,r;
long long val;
}tree[maxn<<2];
void up(int p){
tree[p].val=gcd(abs(tree[p<<1].val),abs(tree[p<<1|1].val));
}
void build(int l,int r,int p){
tree[p].l=l;
tree[p].r=r;
if(l==r)return (void)(tree[p].val=A[l]);
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
up(p);
}
void update(int x,long long pos,int p){
if(tree[p].l==tree[p].r)
return (void)(tree[p].val+=pos);
int mid=tree[p].l+tree[p].r>>1;
if(x<=mid)update(x,pos,p<<1);
else update(x,pos,p<<1|1);
up(p);
}
long long Query(int l,int r,int p){
if(tree[p].l==l&&tree[p].r==r){
return abs(tree[p].val);
}
int mid=tree[p].l+tree[p].r>>1;
if(r<=mid)return Query(l,r,p<<1);
else if(l>mid)return Query(l,r,p<<1|1);
else return gcd(Query(l,mid,p<<1),Query(mid+1,r,p<<1|1));
}
void solve(){
for(int i=1;i<=m;i++)
scanf("%lld",&A[i]);
for(int i=1;i<y;i++)
A[i]-=A[i+1];
for(int i=m;i>y;i--)
A[i]-=A[i-1];
build(1,m,1);
while(q--){
int opt,x1,y1,x2,y2;
scanf("%d%d%d%d%d",&opt,&x1,&y1,&x2,&y2);
if(opt==0){
y1=max(1,y-y1);
y2=min(m,y+y2);
long long ans=Query(y1,y2,1);
printf("%lld\n",ans);
}else {
long long c;
scanf("%lld",&c);
if(y1<=y&&y<=y2)update(y,c,1);
if(y1<=y&&y1>1)update(y1-1,-c,1);
else if(y1>y)update(y1,c,1);
if(y2>=y&&y2<m)update(y2+1,-c,1);
else if(y2<y)update(y2,c,1);
}
}
}
}p60;
struct P100{
static const int maxn=5e5+5;
#define A(i,j) mp[(i-1)*m+j]
struct SegTree{
long long mp[maxn];
int Rt,tot,son[maxn<<4][4];
struct node{
int x1,y1,x2,y2;
long long val;
}tree[maxn<<4];
void up(int rt){
tree[rt].val=gcd(gcd(abs(tree[son[rt][0]].val),abs(tree[son[rt][1]].val)),gcd(abs(tree[son[rt][2]].val),abs(tree[son[rt][3]].val)));
}
void build(int &rt,int x1,int y1,int x2,int y2){
if(x1>x2||y1>y2)return;
rt=++tot;
tree[rt]=(node){x1,y1,x2,y2,0};
if(x1==x2&&y1==y2){
long long pos;
int X=x1,Y=y1;
if(X<x){
if(Y<y)pos=A(X,Y)-A(X+1,Y)-A(X,Y+1)+A(X+1,Y+1);
else if(Y>y)pos=A(X,Y)-A(X+1,Y)-A(X,Y-1)+A(X+1,Y-1);
else pos=A(X,Y)-A(X+1,Y);
}else if(X>x){
if(Y<y)pos=A(X,Y)-A(X-1,Y)-A(X,Y+1)+A(X-1,Y+1);
else if(Y>y)pos=A(X,Y)-A(X-1,Y)-A(X,Y-1)+A(X-1,Y-1);
else pos=A(X,Y)-A(X-1,Y);
}else {
if(Y<y)pos=A(X,Y)-A(X,Y+1);
else if(Y>y)pos=A(X,Y)-A(X,Y-1);
else pos=A(X,Y);
}
return (void)(tree[rt].val=pos);
}
int mx=x1+x2>>1,my=y1+y2>>1;
build(son[rt][0],x1,y1,mx,my);
build(son[rt][1],x1,my+1,mx,y2);
build(son[rt][2],mx+1,y1,x2,my);
build(son[rt][3],mx+1,my+1,x2,y2);
up(rt);
}
void update(int x,int y,long long pos,int rt){
if(tree[rt].x1==tree[rt].x2&&tree[rt].y1==tree[rt].y2)
return (void)(tree[rt].val+=pos);
int mx=tree[rt].x1+tree[rt].x2>>1;
int my=tree[rt].y1+tree[rt].y2>>1;
if(x<=mx){
if(y<=my)update(x,y,pos,son[rt][0]);
else update(x,y,pos,son[rt][1]);
}else {
if(y<=my)update(x,y,pos,son[rt][2]);
else update(x,y,pos,son[rt][3]);
}
up(rt);
}
long long Query(int x1,int y1,int x2,int y2,int rt){
if(tree[rt].x1==0)return 0;
if(tree[rt].x1>=x1&&tree[rt].x2<=x2&&tree[rt].y1>=y1&&tree[rt].y2<=y2){
return abs(tree[rt].val);
}
int mx=tree[rt].x1+tree[rt].x2>>1;
int my=tree[rt].y1+tree[rt].y2>>1;
long long ans=0;
if(x1<=mx&&y1<=my)ans=gcd(Query(x1,y1,x2,y2,son[rt][0]),ans);
if(x1<=mx&&y2>my)ans=gcd(Query(x1,y1,x2,y2,son[rt][1]),ans);
if(x2>mx&&y1<=my)ans=gcd(Query(x1,y1,x2,y2,son[rt][2]),ans);
if(x2>mx&&y2>my)ans=gcd(Query(x1,y1,x2,y2,son[rt][3]),ans);
return ans;
}
void upd(int x,int y,long long pos){
if(x<1||y<1||x>n||y>m)return;
update(x,y,pos,1);
}
}S;
void update(int x1,int y1,int x2,int y2,long long c){
if(x2<x){
if(y2<y){
S.upd(x1-1,y1-1,c);
S.upd(x2,y1-1,-c);
S.upd(x1-1,y2,-c);
S.upd(x2,y2,c);
}else if(y1>y){
S.upd(x1-1,y2+1,c);
S.upd(x1-1,y1,-c);
S.upd(x2,y2+1,-c);
S.upd(x2,y1,c);
}else {
S.upd(x1-1,y1-1,c);
S.upd(x2,y1-1,-c);
S.upd(x1-1,y2+1,c);
S.upd(x2,y2+1,-c);
S.upd(x1-1,y,-c);
S.upd(x2,y,c);
}
}else if(x1>x){
if(y2<y){
S.upd(x2+1,y1-1,c);
S.upd(x1,y1-1,-c);
S.upd(x2+1,y2,-c);
S.upd(x1,y2,c);
}else if(y1>y){
S.upd(x2+1,y2+1,c);
S.upd(x2+1,y1,-c);
S.upd(x1,y2+1,-c);
S.upd(x1,y1,c);
}else {
S.upd(x2+1,y1-1,c);
S.upd(x1,y1-1,-c);
S.upd(x2+1,y2+1,c);
S.upd(x1,y2+1,-c);
S.upd(x1,y,c);
S.upd(x2+1,y,-c);
}
}else {
if(y2<y){
S.upd(x1-1,y1-1,c);
S.upd(x1-1,y2,-c);
S.upd(x2+1,y1-1,c);
S.upd(x2+1,y2,-c);
S.upd(x,y1-1,-c);
S.upd(x,y2,c);
}else if(y1>y){
S.upd(x1-1,y2+1,c);
S.upd(x1-1,y1,-c);
S.upd(x2+1,y2+1,c);
S.upd(x2+1,y1,-c);
S.upd(x,y1,c);
S.upd(x,y2+1,-c);
}else {
S.upd(x1-1,y1-1,c);
S.upd(x1-1,y2+1,c);
S.upd(x2+1,y1-1,c);
S.upd(x2+1,y2+1,c);
S.upd(x,y,c);
S.upd(x,y1-1,-c);
S.upd(x,y2+1,-c);
S.upd(x1-1,y,-c);
S.upd(x2+1,y,-c);
}
}
}
void solve(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&S.A(i,j));
S.build(S.Rt,1,1,n,m);
int opt,x1,y1,x2,y2;
while(q--){
scanf("%d%d%d%d%d",&opt,&x1,&y1,&x2,&y2);
if(opt==1){
long long c;
scanf("%lld",&c);
update(x1,y1,x2,y2,c);
}else {
x1=x-x1;y1=y-y1;
x2=x+x2;y2=y+y2;
printf("%lld\n",S.Query(x1,y1,x2,y2,1));
}
}
}
}p100;
int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
cin>>n>>m>>x>>y>>q;
if(n<=100&&m<=100)p20.solve();
else if(n==1)p60.solve();
else p100.solve();
return 0;
}
```
------
>题外话,注意是动态开点,如果是四叉树则会开成$max(n,m) * max(n,m)$矩阵的线段树了。