题解 UVA11992 【Fast Matrix Operations】
题目描述
有一个r行c列的全0矩阵,有以下三种操作。
-
1 X1 Y1 X2 Y2 v 子矩阵(X1,Y1,X2,Y2)的元素加v
-
2 X1 Y1 X2 Y2 v 子矩阵(X1,Y1,X2,Y2)的元素变为v
-
3 X1 Y1 X2 Y2 查询子矩阵(X1,Y1,X2,Y2)的和,最大值,最小值
子矩阵(X1,Y1,X2,Y2)满足X1<=X<=X2 Y1<=Y<=Y2的所有元素(X1,Y2)。
输入保证和不超过10^9
追加翻译:
数据范围:r <= 20!
主要思路:线段树 + 暴力枚举 + 懒人代码
我在追加翻译中强调了,r <= 20,也就是说,我们可以通过维护20棵线段树来维护矩阵的信息。也就是说,我们的中心任务放在了写一棵区间加,区间修改,维护区间和,区间最大值,最小值的一棵线段树。
…………
代码冗长……但是如何让自己的线段树更短更好写嘞?
首先,在下放标记时,我会调用这样的两个函数:
inline void color_add(int l, int r, int rt, int v) {
z[rt].maxx += v;
z[rt].minn += v;
z[rt].sum += (r - l + 1) * v;
z[rt].col += v;
}
inline void color_chg(int l, int r, int rt, int v) {
z[rt].maxx = z[rt].minn = v;
z[rt].sum = (r - l + 1) * v;
z[rt].coll = v;
z[rt].col = 0;
}
这两个函数分别是用来修改被下放的子节点的信息的。我们这样可以在下放标记时直接调用,甚至可以用来在修改操作时调用,节省大片代码。
然后对于询问操作,我们完全可以直接询问一组数据(一组数据就是一个区间的sum, max, min),我们用结构体实现。对于线段树左右节点的信息合并与询问时的信息合并,我们可以直接通过编写函数来简化代码。(细节就在下文代码里看吧)
我的代码是本题题解截止2019/02/22最短的代码(无注释无过度压行)。
code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define mn 200100
#define inf 1 << 30
#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define bson l, r, rt
inline int read(){
int x = 0, f = 1; char ch = getchar();
while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct tree{
int col, coll, minn, maxx, sum;
tree(int _col = 0, int _coll = -1, int _minn = 0, int _maxx = 0, int _sum = 0)
: col(_col), coll(_coll), minn(_minn), maxx(_maxx), sum(_sum) {}
};
// col -> 区间加的懒标记,coll -> 区间修改的懒标记
// 这里要注意,如果在另一个结构体中调用这个结构体,那么这个结构体最好要初始化一下,否则可能会出现问题。
// 我们用结构体的形式封装线段树,我们可以通过定义SegmentTree类型实现多棵线段树的实现
struct SegmentTree{
tree z[mn << 2];
// 使用外部的结构体类型
inline tree op(tree a, tree b) {
tree res;
res.maxx = max(a.maxx, b.maxx);
res.minn = min(a.minn, b.minn);
res.sum = a.sum + b.sum;
return res;
}
// 这里就是合并信息的懒人函数
inline void update(int rt) {
z[rt] = op(z[rt << 1], z[rt << 1 | 1]);
}
inline void color_add(int l, int r, int rt, int v) {
z[rt].maxx += v;
z[rt].minn += v;
z[rt].sum += (r - l + 1) * v;
z[rt].col += v;
}
inline void color_chg(int l, int r, int rt, int v) {
z[rt].maxx = z[rt].minn = v;
z[rt].sum = (r - l + 1) * v;
z[rt].coll = v;
z[rt].col = 0;
}
inline void push_col(int l, int r, int rt) {
if(z[rt].coll != -1) { // 要注意初始时不能是0
int m = (l + r) >> 1;
color_chg(lson, z[rt].coll);
color_chg(rson, z[rt].coll);
z[rt].coll = -1;
}
if(z[rt].col) {
int m = (l + r) >> 1;
color_add(lson, z[rt].col);
color_add(rson, z[rt].col);
z[rt].col = 0;
}
}
inline void modify(int l, int r, int rt, int nowl, int nowr, int v, int ver) {
if(nowl <= l && r <= nowr) {
if(ver == 1) color_add(bson, v);
else if(ver == 2) color_chg(bson, v);
return;
}
int m = (l + r) >> 1; push_col(bson);
if(nowl <= m) modify(lson, nowl, nowr, v, ver);
if(m < nowr) modify(rson, nowl, nowr, v, ver);
update(rt);
}
inline tree query(int l, int r, int rt, int nowl, int nowr) {
if(nowl <= l && r <= nowr) return z[rt];
int m = (l + r) >> 1; push_col(bson);
if(nowl <= m) {
if(m < nowr) return op(query(lson, nowl, nowr), query(rson, nowl, nowr));
else return query(lson, nowl, nowr);
} else return query(rson, nowl, nowr);
}
inline void build(int l, int r, int rt) {
z[rt].col = 0, z[rt].coll = -1;
if(l == r) {
z[rt].minn = z[rt].maxx = z[rt].sum = 0;
return ;
}
int m = (l + r) >> 1;
build(lson), build(rson), update(rt);
}
// 这里建树代码其实是用来初始化的(多组数据嘛)
} tr[21];
int n, nn, m;
inline void solve() {
go(i, 1, nn, 1) // 每行都要建树
tr[i].build(root);
go(i, 1, m, 1) {
int s = read(), x = read(), y = read(), xx = read(), yy = read(), v;
if(s == 1) {
v = read();
go(j, x, xx, 1) // 对每行的操作
tr[j].modify(root, y, yy, v, 1);
} else if(s == 2) {
v = read();
go(j, x, xx, 1) // 对每行的操作
tr[j].modify(root, y, yy, v, 2);
} else {
int maxx = 0, minn = inf, sum = 0;
go(j, x, xx, 1) { // 对每行的操作
tree res = tr[j].query(root, y, yy);
maxx = max(maxx, res.maxx);
minn = min(minn, res.minn);
sum += res.sum;
}
printf("%d %d %d\n", sum, minn, maxx);
}
}
}
int main(){
while(cin >> nn >> n >> m) {
solve();
}
return 0;
}
希望可以帮助到想简化代码的同学
NOIP2018并不是结束,而是开始