码迷,mamicode.com
首页 > 其他好文 > 详细

题解 UVA11992 【Fast Matrix Operations】

时间:2019-03-23 00:53:17      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:read   初始化   ati   upd   namespace   ==   lse   min   name   

题目描述

有一个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;
}

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

题解 UVA11992 【Fast Matrix Operations】

标签:read   初始化   ati   upd   namespace   ==   lse   min   name   

原文地址:https://www.cnblogs.com/yizimi/p/10582027.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!