NOI 2012 魔幻棋盘 | 二维差分 + 二维线段树
题目:luogu 2086
二维线段树,按套路差分原矩阵,gcd( x1, x2, ……, xn ) = gcd( xi , x2 - x1 , ……, xn - xn-1 ),必须要有一个原数 xi,恰好每次询问都包含一个固定点 ( X , Y ),差分以它为中心就可以保证它是原值。以 e 为中心的二维差分如图。
对于一维序列,修改区间 [ l , r ] 只需修改差分后的 l 和 r + 1 两点。那么对于二维,差分后的修改如下所示:
中间的灰色格子为守卫者所在地(为方便表示多个区域把它拆成了 9 格),以它为差分中心,若修改矩阵在它的右下方,即黄色区域,设它的左上角坐标为 ( x1, y1),右下角坐标 ( x2, y2 ),则需在 ( x1, y1 ) 处 + c,( x1, y2+1 ) 处 - c,( x2+1, y1 ) 处 - c,( x2 + 1, y2 + 1) 处 +c。
其他区域同理。再加上守卫者正上方、正下方、正左方、正右方的区域,一共 8 种情况。如果一个修改矩阵包含多个区域,就将其划分为若干个子矩阵处理。
这样就将巨慢的区间修改转换成了单点修改,每修改完一个点,pushup( ) 维护 gcd 即可。
另外读入的时候,500000 * 500000 的二维数组是开不了的,需要动态分配内存;或者开一个 500000 的一维数组读入,判断每一行的末端在哪里。
讨论的时候有些麻烦,下面列一下大致的情况:(黄色表示修改的区域,绿色加, 红色减)
仔细观察后,发现可分为三种情况:① 包含守卫者 ( X , Y );② 包含 X 行或 Y 列;③ 只包含左上或左下、右上或右下区域。
细节诸多:
差分的时候不能用原数组减,因为前面的值会变,需要另开一个数组保存;
返回 gcd 的时候要取绝对值;二维线段树内层空间要开大。
#include <cstdio>
#include <string> const int N = ;
typedef long long ll; int n, m; ll a[N], b[N]; ll read() {
ll x = , f = ;
char c = getchar();
while (!isdigit(c)) {
if (c == '-') f = -;
c = getchar();
}
while (isdigit(c)) {
x = (x << ) + (x << ) + (c ^ );
c = getchar();
}
return x * f;
} ll abs(ll x) {
if (x >= ) return x; return -x;
} ll gcd(ll x, ll y) {
if (y) return gcd(y, x % y); return abs(x);
} struct inNode {
ll val;
inNode *ls, *rs;
inNode() : val(), ls(NULL), rs(NULL) {}
} inPool[N << ]; inNode *newInNode () {
static int cnt = ;
return &inPool[++cnt];
} void buildY(inNode *&cur, int l, int r, int x) {
if (!cur) cur = newInNode();
if (l == r) cur->val = a[(x - ) * m + l];
else {
int mid = l + ((r - l) >> );
buildY(cur->ls, l, mid, x);
buildY(cur->rs, mid + , r, x);
cur->val = gcd(cur->ls->val, cur->rs->val);
}
} void updateY(inNode *&cur, int l, int r, int y, ll val) {
if (l == r) cur->val += val;
else {
int mid = l + ((r - l) >> );
if (y <= mid) updateY(cur->ls, l, mid, y, val);
else updateY(cur->rs, mid + , r, y, val);
cur->val = gcd(cur->ls ? cur->ls->val : , cur->rs ? cur->rs->val : );
}
} ll queryY(inNode *&cur, int l, int r, int y1, int y2) {
if (y1 <= l && r <= y2) return cur->val;
int mid = l + ((r - l) >> ); ll res = ;
if (y1 <= mid) res = gcd(res, queryY(cur->ls, l, mid, y1, y2));
if (mid < y2) res = gcd(res, queryY(cur->rs, mid + , r, y1, y2));
return res;
} struct outNode {
inNode *root;
outNode *ls, *rs;
outNode() : root(NULL), ls(NULL), rs(NULL) {}
} outPool[N << ], *root; outNode *newOutNode() {
static int cnt = ;
return &outPool[++cnt];
} void maintain(inNode *&cur, inNode *&lc, inNode *&rc, int l, int r) {
if (!cur) cur = newInNode();
if (l == r) cur->val = gcd(lc->val, rc->val);
else {
int mid = l + ((r - l) >> );
maintain(cur->ls, lc->ls, rc->ls, l, mid);
maintain(cur->rs, lc->rs, rc->rs, mid + , r);
cur->val = gcd(cur->ls->val ,cur->rs->val);
}
} void buildX(outNode *&cur, int l, int r) {
if (!cur) cur = newOutNode();
if (l == r) buildY(cur->root, , m, l);
else {
int mid = l + ((r - l) >> );
buildX(cur->ls, l, mid);
buildX(cur->rs, mid + , r);
maintain(cur->root, cur->ls->root, cur->rs->root, , m);
}
} void change(inNode *&cur, int l, int r, int y, ll val) {
if (l == r) cur->val = val;
else {
int mid = l + ((r - l) >> );
if (y <= mid) change(cur->ls, l, mid, y, val);
else change(cur->rs, mid + , r, y, val);
cur->val = gcd(cur->ls ? cur->ls->val : , cur->rs ? cur->rs->val : );
}
} void updateX(outNode *&cur, int l, int r, int x, int y, ll val) {
if (x <= || y <= || x > n || y > m) return;
if (l == r) updateY(cur->root, , m, y, val);
else {
int mid = l + ((r - l) >> ); ll lv = , rv = ;
if (x <= mid) updateX(cur->ls, l, mid, x, y, val);
else updateX(cur->rs, mid + , r, x, y, val);
if (cur->ls) lv = queryY(cur->ls->root, , m, y, y);
if (cur->rs) rv = queryY(cur->rs->root, , m, y, y);
change(cur->root, , m, y, gcd(lv, rv));
}
} ll queryX(outNode *&cur, int l, int r, int x1, int x2, int y1, int y2) {
if (x1 <= l && r <= x2) return queryY(cur->root, , m, y1, y2);
int mid = l + ((r - l) >> ); ll res = ;
if (x1 <= mid) res = gcd(res, queryX(cur->ls, l, mid, x1, x2, y1, y2));
if (mid < x2) res = gcd(res, queryX(cur->rs, mid + , r, x1, x2, y1, y2));
return res;
} int main() {
n = read(), m = read(); int X = read(), Y = read(), T = read();
for (int i = ; i <= n * m; ++ i) a[i] = read();
for (int i = ; i <= n * m; ++ i) {
if ((i - ) % m + < Y) b[i] = a[i] - a[i + ];
else if ((i - ) % m + > Y) b[i] = a[i] - a[i - ];
else b[i] = a[i];
}
for (int i = ; i <= n * m; ++ i) {
if ((i - ) / m + < X) a[i] = b[i] - b[i + m];
else if ((i - ) / m + > X) a[i] = b[i] - b[i - m];
else a[i] = b[i];
}
buildX(root, , n);
while (T--) {
int opt = read(), x1 = read(), y1 = read(), x2 = read(), y2 = read();
if (opt == ) {
x1 = X - x1, x2 = X + x2, y1 = Y - y1, y2 = Y + y2;
printf("%lld\n", queryX(root, , n, x1, x2, y1, y2));
} else {
ll val = read();
if (x1 <= X && x2 >= X && y1 <= Y && y2 >= Y) { //包含(X,Y)
updateX(root, , n, X, Y, val);
updateX(root, , n, x1 - , y1 - , val);
updateX(root, , n, x1 - , y2 + , val);
updateX(root, , n, x2 + , y1 - , val);
updateX(root, , n, x2 + , y2 + , val);
updateX(root, , n, x1 - , Y, -val);
updateX(root, , n, x2 + , Y, -val);
updateX(root, , n, X, y1 - , -val);
updateX(root, , n, X, y2 + , -val);
} else if (y1 <= Y && y2 >= Y) { //包含Y列
if (x1 < X) { //在(X,Y)上方
updateX(root, , n, x2, Y, val);
updateX(root, , n, x1 - , y1 - , val);
updateX(root, , n, x1 - , y2 + , val);
updateX(root, , n, x1 - , Y, -val);
updateX(root, , n, x2, y1 - , -val);
updateX(root, , n, x2, y2 + , -val);
} else { //在(X,Y)下方
updateX(root, , n, x1, Y, val);
updateX(root, , n, x2 + , y1 - , val);
updateX(root, , n, x2 + , y2 + , val);
updateX(root, , n, x2 + , Y, -val);
updateX(root, , n, x1, y1 - , -val);
updateX(root, , n, x1, y2 + , -val);
}
} else if (x1 <= X && x2 >= X) { //包含X行
if (y1 < Y) { //在(X,Y)左边
updateX(root, , n, X, y2, val);
updateX(root, , n, x1 - , y1 - , val);
updateX(root, , n, x2 + , y1 - , val);
updateX(root, , n, X, y1 - , -val);
updateX(root, , n, x1 - , y2, -val);
updateX(root, , n, x2 + , y2, -val);
} else { //在(X,Y)右边
updateX(root, , n, X, y1, val);
updateX(root, , n, x1 - , y2 + , val);
updateX(root, , n, x2 + , y2 + , val);
updateX(root, , n, X, y2 + , -val);
updateX(root, , n, x1 - , y1, -val);
updateX(root, , n, x2 + , y1, -val);
}
} else if (x1 < X && y1 < Y) { //左上区域
updateX(root, , n, x2, y2, val);
updateX(root, , n, x1 - , y1 - , val);
updateX(root, , n, x1 - , y2, -val);
updateX(root, , n, x2, y1 - , -val);
} else if (x1 < X && y1 > Y) { //右上区域
updateX(root, , n, x2, y1, val);
updateX(root, , n, x1 - , y2 + , val);
updateX(root, , n, x1 - , y1, -val);
updateX(root, , n, x2, y2 + , -val);
} else if (x1 > X && y1 < Y) { //左下区域
updateX(root, , n, x1, y2, val);
updateX(root, , n, x2 + , y1 - , val);
updateX(root, , n, x1, y1 - , -val);
updateX(root, , n, x2 + , y2, -val);
} else { //右下区域
updateX(root, , n, x1, y1, val);
updateX(root, , n, x2 + , y2 + , val);
updateX(root, , n, x1, y2 + , -val);
updateX(root, , n, x2 + , y1, -val);
}
}
}
return ;
}
NOI 2012 魔幻棋盘 | 二维差分 + 二维线段树的更多相关文章
- 【坐标变换】【二维偏序】【线段树】Gym - 100820G - Racing Gems
题意:第一象限有n个点,你从x正半轴任选一个位置出发,vy恒定,vx可以任意变化,不过只能在-vy/r到vy/r之间变化,问你最多能经过多少个点. 暴力dp是n^2,不可取. 注意到,一个点,所能到达 ...
- [BZOJ3196] 二逼平衡树 [权值线段树套位置平衡树]
题面 洛咕题面 思路 没错我就是要不走寻常路! 看看那些外层位置数据结构,必须二分的,$O(n\log^3 n)$的做法吧! 看看那些cdq分治/树状数组套线段树的,空间$O(n\log^2 n)$挤 ...
- 二叉搜索树TREE(线段树,区间DP)
前言 线段树+区间DP题,线段树却不是优化DP的,是不是很意外? 题面 二叉搜索树是一种二叉树,每个节点都有一个权值,并且一个点的权值比其左子树里的点权值都大,比起右子树里的点权值都小. 一种朴素的向 ...
- [BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)
题目传送门 题目描述: N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式: 第一 ...
- 洛谷1083(差分+二分 or 线段树)
第一种方法:可以二分最大天数订单的答案然后通过差分求一下是否可行. ; int n, m, a[maxn], ans; struct section { int cnt, l, r; }b[maxn] ...
- 2019杭电多校6 hdu6638 Snowy Smile(二维最大矩阵和 线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:给你一些点的权值,让找一个矩形圈住一部分点,问圈住点的最大权值和 分析:由于是稀疏图,明显要先把x, ...
- 刷题总结——二逼平衡树(bzoj3224线段树套splay)
题目: Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在 ...
- 2018HDU多校二 -F 题 Naive Operations(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6315 In a galaxy far, far away, there are two integer ...
- NOIp 2012 #2 借教室 Label:区间修改线段树
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然 ...
随机推荐
- JavaScript 推断浏览器类型及32位64位
JS推断出版本号以及浏览器类型 <script type="text/javascript"> var Sys = {}; var ua = navigator.use ...
- 赞一下TMS Software 和 AdvStringGrid
非常久前给Support发Email问能不能在设计期给AdvStringGrid标题加个数字标识.每次我都是自己改代码加上去.这次升级到新版本号,没想到增加了这个功能: 功能虽小可是非常有用,非常多的 ...
- Naive Bayesian文本分类器
贝叶斯学习方法中有用性非常高的一种为朴素贝叶斯学习期,常被称为朴素贝叶斯分类器. 在某些领域中与神经网络和决策树学习相当.尽管朴素贝叶斯分类器忽略单词间的依赖关系.即如果全部单词是条件独立的,但朴素贝 ...
- POJ2376 Cleaning Shifts 【贪心】
Cleaning Shifts Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11542 Accepted: 3004 ...
- AMD单桥主板上电时序的详细解释
3个待机条件: 1.桥需要得到待机电压:3.3V,1.5V/1.2V2.25M起振注:NV的RTC电路,一般不会导致时序故障,都可以出CPURST#3.PWRGD-SB(即INTEL芯片组的RSMRS ...
- git 忽略文件的三种方式
1. 在项目目录下新建.gitignore文件并添加规则 特点:此种方式的忽略规则只局限于本项目目录及其子目录,并且.gitignore文件会被提交到远程仓库进行共享忽略规则. 2. 在.git/in ...
- 通过路由管理视图间切换 - AngularJS路由解析
模板的视图刷新 ng-view这个指令和路由组合之后就可以将$route对应的视图放入指定的HTML中,这一过程中它会创建自己的作用域并将模板嵌套在内部. ng-view指令的优先级是1000(终极) ...
- ANDROID STUDIO 2.2.3 DOWNLOAD FROM DL.GOOGLE.COM
立即开始使用 Android Studio Android Studio 包含用于构建 Android 应用所需的所有工具. 下载 ANDROID STUDIO2.2.3 FOR WINDOWS (1 ...
- HDU 5384 Danganronpa (2015年多校比赛第8场)
1.题目描写叙述:点击打开链接 2.解题思路:本题利用字典树解决.本题要求查找全部的B[j]在A[i]中出现的总次数.那么我们能够建立一颗字典树,将全部的B[j]插入字典树,因为一个串的全部字串相当于 ...
- 您的安全性偏好设置仅允许安装来自 App Store 和被认可的开发者的应用
您的安全性偏好设置仅允许安装来自 App Store 和被认可的开发者的应用. 安装macOS Sierra后,会发现系统偏好设置的“安全与隐私”中默认已经去除了允许“任何来源”App的选项,无法运行 ...