UVA11992 Fast Matrix Operations
前置芝士
- 线段树
- 多颗线段树
题目大意
需要对一个初始全为0的矩阵进行操作。给定两个点,并执行下面三个操作:
- 对给定两个点中间的范围加上
val
。 - 将给定两个点中间的范围全部赋值为
val
。 - 询问给定两个点中间的范围中所有元素的和和其中的最大值与最小值。
矩阵不超过20行。
思路
因为矩阵不超过20行,可以考虑每一行直接开一棵线段树进行维护。
区间加法与查询
区间加法和查询很简单,只需要参照正常线段树,一颗一颗维护就行。
- 要注意的一点,区间加法对于最大值和最小值也需要同时加上。
区间赋值
对于区间赋值,需要注意其与区间加法和维护区间最值得关系:
- 区间赋值的更新必须要在区间加法之前,且区间加法的标记在区间赋值进行后需要清空(不管之前加了多少值赋值一下就没了正常吧)。
- 区间赋值的时候区间最值也要直接更改。
开多颗线段树
因为要开多棵树,因此将线段树直接包装成类会更好(但是代码量大涨)。
具体代码实现
一种可行的代码实现如下。
下面这种可行的线段树模板用的是动态开点而不是堆式存储。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class seg {
private:
struct Node {
ll val;
ll minn, maxn;
ll mkad, mkch;
int lc, rc;
Node() {
val = mkad = mkch = lc = rc = 0;
mkad = mkch = 0;
}
};
void update(int now) {
tr[now].val = tr[tr[now].lc].val + tr[tr[now].rc].val;
tr[now].minn = min(tr[tr[now].lc].minn, tr[tr[now].rc].minn);
tr[now].maxn = max(tr[tr[now].lc].maxn, tr[tr[now].rc].maxn);
}
void pushdown(int pll, int prr, int now) {
int mid = pll + ((prr - pll) >> 1);
if (tr[now].mkch) {
tr[tr[now].lc].val = (mid - pll + 1) * tr[now].mkch;
tr[tr[now].lc].maxn = tr[tr[now].lc].minn = tr[now].mkch;
tr[tr[now].lc].mkch = tr[now].mkch;
tr[tr[now].rc].val = (prr - mid) * tr[now].mkch;
tr[tr[now].rc].maxn = tr[tr[now].rc].minn = tr[now].mkch;
tr[tr[now].rc].mkch = tr[now].mkch;
tr[tr[now].lc].mkad = tr[tr[now].rc].mkad = 0;
tr[now].mkch = 0;
}
if (tr[now].mkad) {
tr[tr[now].lc].val += (mid - pll + 1) * tr[now].mkad;
tr[tr[now].lc].maxn += tr[now].mkad;
tr[tr[now].lc].minn += tr[now].mkad;
tr[tr[now].lc].mkad += tr[now].mkad;
tr[tr[now].rc].val += (prr - mid) * tr[now].mkad;
tr[tr[now].rc].maxn += tr[now].mkad;
tr[tr[now].rc].minn += tr[now].mkad;
tr[tr[now].rc].mkad += tr[now].mkad;
tr[now].mkad = 0;
}
}
vector<Node>tr;
int tot, root;
int n;
void build(int pll, int prr, int now) {
if (pll == prr) {
tr[now].maxn = tr[now].minn = 0;
tr[now].lc = tr[now].rc = 0;
tr[now].val = tr[now].mkad = tr[now].mkch = 0;
return;
}
int mid = pll + ((prr - pll) >> 1);
tr[now].lc = ++ tot;
build(pll, mid, tr[now].lc);
tr[now].rc = ++ tot;
build(mid + 1, prr, tr[now].rc);
update(now);
}
void add(int sll, int srr, ll val, int pll, int prr, int now) {
if (sll <= pll && srr >= prr) {
tr[now].val += (prr - pll + 1) * val;
tr[now].maxn += val;
tr[now].minn += val;
tr[now].mkad += val;
return;
}
int mid = pll + ((prr - pll) >> 1);
pushdown(pll, prr, now);
if (sll <= mid) {
add(sll, srr, val, pll, mid, tr[now].lc);
}
if (srr > mid) {
add(sll, srr, val, mid + 1, prr, tr[now].rc);
}
update(now);
}
void change(int sll, int srr, ll val, int pll, int prr, int now) {
if (sll <= pll && srr >= prr) {
tr[now].val = (prr - pll + 1) * val;
tr[now].maxn = tr[now].minn = val;
tr[now].mkch = val;
tr[now].mkad = 0;
return;
}
int mid = pll + ((prr - pll) >> 1);
pushdown(pll, prr, now);
if (sll <= mid) {
change(sll, srr, val, pll, mid, tr[now].lc);
}
if (srr > mid) {
change(sll, srr, val, mid + 1, prr, tr[now].rc);
}
update(now);
}
tuple<ll, ll, ll> query(int sll, int srr, int pll, int prr, int now) {
if (sll <= pll && srr >= prr) {
return make_tuple(tr[now].val, tr[now].maxn, tr[now].minn);
}
int mid = pll + ((prr - pll) >> 1);
pushdown(pll, prr, now);
ll sum = 0, maxn = 0, minn = 1 << 30;
if (sll <= mid) {
auto [a, b, c] = query(sll, srr, pll, mid, tr[now].lc);
sum += a; maxn = max(maxn, b); minn = min(minn, c);
}
if (srr > mid) {
auto [a, b, c] = query(sll, srr, mid + 1, prr, tr[now].rc);
sum += a; maxn = max(maxn, b); minn = min(minn, c);
}
return make_tuple(sum, maxn, minn);
}
public:
void inti(int siz) {
n = siz;
tr.assign(siz << 4, Node());
tot = root = 1;
build(1, n, root);
}
void add(int sll, int srr, ll val) {
add(sll, srr, val, 1, n, root);
}
void change(int sll, int srr, ll val) {
change(sll, srr, val, 1, n, root);
}
tuple<ll, ll, ll> query(int sll, int srr) {
return query(sll, srr, 1, n, root);
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int r, c, t;
cin >> r >> c >> t;
vector<seg>tr(r + 1);
for (int i = 1; i <= r; ++ i) {
tr[i].inti(c);
}
while (t --) {
int c, x1, y1, x2, y2;
ll val;
cin >> c >> x1 >> y1 >> x2 >> y2;
if (c == 1) {
cin >> val;
for (int i = x1; i <= x2; ++ i) {
tr[i].add(y1, y2, val);
}
} else if (c == 2) {
cin >> val;
for (int i = x1; i <= x2; ++ i) {
tr[i].change(y1, y2, val);
}
} else {
ll sum = 0, maxn = 0, minn = 1 << 30;
for (int i = x1; i <= x2; ++ i) {
auto [a, b, c] = tr[i].query(y1, y2);
sum += a; maxn = max(maxn, b); minn = min(minn, c);
}
cout << sum << ' ' << minn << ' ' << maxn << '\n';
}
}
return 0;
}