题解 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;
}

希望可以帮助到想简化代码的同学

posted @ 2019-03-22 23:17  yizimi  阅读(129)  评论(0编辑  收藏  举报