http://www.lydsy.com/JudgeOnline/problem.php?id=2877 (题目链接)

题意

  一个${n*m}$的矩阵,维护两个操作:给任意子矩阵${+val}$;问某一包含点${(X,Y)}$的矩阵内元素的gcd。

Solution

  左转题解,参见PoPoQQQ,写的蛮详细的,像我这种没写过树套树的都会了→_→。

  代码也是模着PoPoQQQ大爷的写的,边界真的好蛋疼。

  对于不能用以任意一点为差分中心的原因,我有一点自己的看法。我们考虑之所以差分后的数组的gcd与原数组的gcd相等是为什么,不是更相减损吗,考虑一维的情况,如果我们本来要求${gcd(a_2,a_3,a_4,a_5)}$,我们以${a_1}$为中心差分的话,那么就成了求解差分数组的${gcd(a_2-a_1,a_3-a_2,a_4-a_3,a_5-a_4)}$,这就不符合更相减损术了,因为里面莫名其妙混进了个${a_1}$。为了避免这种情况,我们以一定包含在询问矩阵内的${(X,Y)}$为中心差分,这就保证了差分后每一个询问矩阵中不包含在矩阵外的原矩阵的元素。也就是说,如果没有${(X,Y)}$一定包含在询问矩阵中这个条件的话,这个题应该是做不了的。

细节

  这里并没有给出${n,m}$的具体范围,所以我们需要使用黑科技读入矩阵。

  LL,边界的分类讨论注意别写错点的坐标。

代码

// bzoj2877
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=500010;
int n,m,Q,X,Y,rt,sz; struct segtree {int l,r,ls,rs,tree;LL val;}tr[maxn<<4];
struct array { //来自PoPoQQQ的黑科技
LL t[maxn];
LL* operator [] (int x) {return &t[(x-1)*m];}
}a; LL gcd(LL a,LL b) {
return b==0 ? a : gcd(b,a%b);
} namespace Col {
void build(int &k,int s,int t,int p) {
k=++sz;tr[k].l=s,tr[k].r=t;
if (s==t) {tr[k].val=a[p][s];return;}
int mid=(s+t)>>1;
build(tr[k].ls,s,mid,p);
build(tr[k].rs,mid+1,t,p);
tr[k].val=gcd(tr[tr[k].ls].val,tr[tr[k].rs].val);
}
void modify(int k,int p,LL val) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==r) {tr[k].val+=val;return;}
if (p<=mid) modify(tr[k].ls,p,val);
else modify(tr[k].rs,p,val);
tr[k].val=gcd(tr[tr[k].ls].val,tr[tr[k].rs].val);
}
LL query(int k,int s,int t) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==s && r==t) return tr[k].val;
if (t<=mid) return query(tr[k].ls,s,t);
else if (s>mid) return query(tr[k].rs,s,t);
else return gcd(query(tr[k].ls,s,mid),query(tr[k].rs,mid+1,t));
}
} namespace Row {
void merge(int &k,int k1,int k2,int s,int t) {
if (!k) k=++sz,tr[k].l=s,tr[k].r=t;
int mid=(s+t)>>1;
if (s==t) {tr[k].val=gcd(tr[k1].val,tr[k2].val);return;}
merge(tr[k].ls,tr[k1].ls,tr[k2].ls,s,mid);
merge(tr[k].rs,tr[k1].rs,tr[k2].rs,mid+1,t);
tr[k].val=gcd(tr[tr[k].ls].val,tr[tr[k].rs].val);
}
void build(int &k,int s,int t) {
k=++sz;tr[k].l=s,tr[k].r=t;
if (s==t) {Col::build(tr[k].tree,1,m,s);return;}
int mid=(s+t)>>1;
build(tr[k].ls,s,mid);
build(tr[k].rs,mid+1,t);
merge(tr[k].tree,tr[tr[k].ls].tree,tr[tr[k].rs].tree,1,m);
}
void modify(int k,int x,int y,LL val) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==r) {Col::modify(tr[k].tree,y,val);return;}
if (x<=mid) modify(tr[k].ls,x,y,val);
else modify(tr[k].rs,x,y,val);
LL lval=Col::query(tr[tr[k].ls].tree,y,y);
LL rval=Col::query(tr[tr[k].rs].tree,y,y);
LL mval=Col::query(tr[k].tree,y,y);
Col::modify(tr[k].tree,y,gcd(lval,rval)-mval);
}
LL query(int k,int x1,int y1,int x2,int y2) {
int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
if (l==x1 && r==x2) return Col::query(tr[k].tree,y1,y2);
if (x2<=mid) return query(tr[k].ls,x1,y1,x2,y2);
else if (x1>mid) return query(tr[k].rs,x1,y1,x2,y2);
else return gcd(query(tr[k].ls,x1,y1,mid,y2),query(tr[k].rs,mid+1,y1,x2,y2));
}
}
using namespace Row; int main() {
scanf("%d%d%d%d%d",&n,&m,&X,&Y,&Q);
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<Y;j++) a[i][j]-=a[i][j+1];
for (int j=m;j>Y;j--) a[i][j]-=a[i][j-1];
}
for (int j=1;j<=m;j++) {
for (int i=1;i<X;i++) a[i][j]-=a[i+1][j];
for (int i=n;i>X;i--) a[i][j]-=a[i-1][j];
}
build(rt,1,n);
for (int op,x1,y1,x2,y2,i=1;i<=Q;i++) {
scanf("%d%d%d%d%d",&op,&x1,&y1,&x2,&y2);
if (op==0) {
x1=X-x1;y1=Y-y1;x2=X+x2;y2=Y+y2;
printf("%lld\n",abs(query(rt,x1,y1,x2,y2)));
}
else {
LL val;scanf("%lld",&val);
//左上端点(x1,y1)
if (x1<=X && y1<=Y && x1>1 && y1>1) modify(rt,x1-1,y1-1,val);
else if (x1<=X && y1>Y && x1>1) modify(rt,x1-1,y1,-val);
else if (x1>X && y1<=Y && y1>1) modify(rt,x1,y1-1,-val);
else if (x1>X && y1>Y) modify(rt,x1,y1,val);
//右上端点(x1,y2)
if (x1<=X && y2>=Y && x1>1 && y2<m) modify(rt,x1-1,y2+1,val);
else if (x1<=X && y2<Y && x1>1) modify(rt,x1-1,y2,-val);
else if (x1>X && y2>=Y && y2<m) modify(rt,x1,y2+1,-val);
else if (x1>X && y2<Y) modify(rt,x1,y2,val);
//左下端点(x2,y1)
if (x2>=X && y1<=Y && x2<n && y1>1) modify(rt,x2+1,y1-1,val);
else if (x2<X && y1<=Y && y1>1) modify(rt,x2,y1-1,-val);
else if (x2>=X && y1>Y && x2<n) modify(rt,x2+1,y1,-val);
else if (x2<X && y1>Y) modify(rt,x2,y1,val);
//右下端点(x2,y2)
if (x2>=X && y2>=Y && x2<n && y2<m) modify(rt,x2+1,y2+1,val);
else if (x2>=X && y2<Y && x2<n) modify(rt,x2+1,y2,-val);
else if (x2<X && y2>=Y && y2<m) modify(rt,x2,y2+1,-val);
else if (x2<X && y2<Y) modify(rt,x2,y2,val); if (x1<=X && x2>=X) {
//左端点(X,y1)
if (y1<=Y && y1>1) modify(rt,X,y1-1,-val);
else if (y1>Y) modify(rt,X,y1,val);
//右端点(X,y2)
if (y2>=Y && y2<m) modify(rt,X,y2+1,-val);
else if (y2<Y) modify(rt,X,y2,val);
} if (y1<=Y && y2>=Y) {
//上端点
if (x1<=X && x1>1) modify(rt,x1-1,Y,-val);
else if (x1>X) modify(rt,x1,Y,val);
//下端点
if (x2>=X && x2<n) modify(rt,x2+1,Y,-val);
else if (x2<X) modify(rt,x2,Y,val);
} if (x1<=X && x2>=X && y1<=Y && y2>=Y) modify(rt,X,Y,val);
}
}
return 0;
}

【bzoj2877】 Noi2012—魔幻棋盘的更多相关文章

  1. BZOJ2877 NOI2012魔幻棋盘(二维线段树)

    显然一个序列的gcd=gcd(其差分序列的gcd,序列中第一个数).于是一维情况直接线段树维护差分序列即可. 容易想到将该做法拓展到二维.于是考虑维护二维差分,查询时对差分矩阵求矩形的gcd,再对矩形 ...

  2. [BZOJ2877][NOI2012]魔幻棋盘(二维线段树)

    https://blog.sengxian.com/solutions/bzoj-2877 注意二维线段树的upd()也是一个O(log n)的函数(pushdown()应该也是但没写过). #inc ...

  3. BZOJ2877 [Noi2012]魔幻棋盘

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  4. BZOJ2877:[NOI2012]魔幻棋盘

    浅谈树状数组与主席树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://lydsy.com/JudgeOnline/problem. ...

  5. 2877: [Noi2012]魔幻棋盘 - BZOJ

    DescriptionInput 第一行为两个正整数N,M,表示棋盘的大小. 第二行为两个正整数X,Y,表示棋盘守护者的位置. 第三行仅有一个正整数T,表示棋盘守护者将进行次操作. 接下来N行,每行有 ...

  6. NOI2012 魔幻棋盘

    http://www.lydsy.com/JudgeOnline/problem.php?id=2877 二维线段树. 好恶...... B类数据: 棋盘是一维的. 我们有一个结论: $gcd(a_{ ...

  7. 题解 洛谷 P2086 【[NOI2012]魔幻棋盘】

    先考虑只有一维的情况,要求支持区间加和求区间 \(\gcd\),根据 \(\gcd\) 的性质,发现: \[ \gcd(a_1,a_2,a_3,\ldots a_n)=\gcd(a_i,a_2-a_1 ...

  8. 数据结构(二维线段树,差分): NOI2012 魔幻棋盘

    貌似想复杂了…… #include <iostream> #include <cstring> #include <cstdio> #define mid ((l+ ...

  9. 【NOI2012】魔幻棋盘

    Description 将要读二年级的小 Q 买了一款新型益智玩具——魔幻棋盘,它是一个N行M列的网格棋盘,每个格子中均有一个正整数.棋盘守护者在棋盘的第X行Y列(行与列均从1开始编号) 并且始终不会 ...

随机推荐

  1. 20155323刘威良《网络对抗》Exp5 MSF基础应用

    20155323刘威良<网络对抗>Exp5 MSF基础应用 实践内容 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路.具体需要完成: 1.1一个主动攻击实 ...

  2. linux & windows下重启oracle

    Linux:方法1 用root以ssh登录到linux,打开终端输入以下命令: cd $ORACLE_HOME #进入到oracle的安装目录 dbstart #重启服务器 lsnrctl start ...

  3. 总结:C# 委托的全面理解

    在说事件之前得先了解委托. 委托,外表看来和C/C++中函数指针没什么区别,但是本质上你才发现他其实就是个类!也就是说理解委托得从 这个两个方面去理解(单从一个方面去理解感觉就怪怪的呵呵!) 理解委托 ...

  4. 重置Oracle配置

    经常被ORACLE坑,作为一个只需要开发时候连连ORACLE的程序员,在经历了一次又一次的折腾之后,决定还是把这些琐碎的事情写下来. 经常在虚拟机中使用ORACLE,ORACLE的网络配置有一些变化就 ...

  5. 通过Mysql连接ASP.Net Core2.0(Code First模式)

    ASP.NET Core2.0连接Mysql,首先新建项目 选择Web应用程序 选择需要身份验证: 通过Nuget安装Mysql驱动,这里推荐>Pomelo.EntityFrameworkCor ...

  6. jmeter:正则表达式的使用

    Jmeter中正则关联的使用是可以提取动态变化数据进行传递:关联的方式和提取器有多种,这篇先讲解正则表达式怎么来关联(?) 在需要获取数据的http请求上添加后置处理器 比如提取百度title值: 正 ...

  7. jmeter实战1

  8. 使用VSCode调试单个PHP文件

    突然发现是可以使用 VSCode 调试单个 PHP 文件的,今天之前一直没有弄成功,还以为 VSCode 是不能调试单文件呢.这里记录一下今天这个"突然发现"的过程. 开始,是在看 ...

  9. 现已告别五险一金?迎来社保商保时代保险INSURAUNCE

    现已告别五险一金?迎来社保商保时代保险INSURAUNCE 经济工作会议提出,中国要降低社会保险费,研究精简归并"五险一金",可以说是为社保变革指明了大方向.未来,生育保险将与基本 ...

  10. 金蝶K3 11.0 WISE版本盘点机PDA条码数据采集器仓库条码管理