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. 20155217《网络对抗》Exp09 Web安全基础实践

    20155217<网络对抗>Exp09 Web安全基础实践 实践内容 关于webgoat:询问了很多人在安装webgoat时出现了错误,安装失败,因此直接通过同学copy了老师的虚拟机进行 ...

  2. 2017-2018-2 20155234『网络对抗技术』Exp5:MSF基础应用

    攻击MS08-067安全漏洞--WindowsXP 在kali输入msfconsole进入控制台,依次输入指令 第一次很遗憾失败了 发现是防火墙没关重新来过成功 攻击MS11-050安全漏洞--IE7 ...

  3. 20155311《网络对抗》MSF基础应用

    20155311<网络对抗>MSF基础应用 实验过程 实验系统 靶机1:Windows XP Professional SP2 ,IP地址:192.168.136.129 靶机2:Wind ...

  4. Android开发——Fragment知识整理(二)

    0.  前言 Android开发中的Fragment的应用非常广泛,在Android开发--Fragment知识整理(一)中简单介绍了关于Fragment的生命周期,常用API,回退栈的应用等知识.这 ...

  5. Centos7下python3安装pip-9.0.1

    pip类似RedHat里面的yum,安装Python包非常方便.本节详细介绍pip的安装.以及使用方法 1.下载pip安装包 [root@localhost ~]# wget https://pypi ...

  6. 如何在web api中使用SignalR

    说明: 在webapi中使用signalr,使用IIS 环境: vs2012, .net4.5 第一步:建web api项目 第二步:nuget导入signalr Install-Package Mi ...

  7. winform 记事本 剪切 粘贴 全选 撤销

    private void 撤消UToolStripMenuItem_Click(object sender, EventArgs e) { textBox1.Undo(); } private voi ...

  8. java十年,需要学会的Java开发体系

    阿里十年,只剩下这套Java开发体系了,链接:https://www.jianshu.com/p/ca6c4a73aac9

  9. JQ_开发经验

    1. 把你的代码全部放在闭包里面 这是我用的最多的一条.但是有时候在闭包外面的方法会不能调用.不过你的插件的代码只为你自己的插件服务,所以不存在这个问题,你可以把所有的代码都放在闭包里面.而方法可能应 ...

  10. eclipse中设置项目的编码方式

    1.windows->Preferences...打开"首选项"对话框,左侧导航树,导航到general->Workspace,右侧Text file encoding ...