UVA ~ 11992 ~ Fast Matrix Operations (线段树 + 区间更新)

题意:有一个r行c列的全0矩阵,支持以下3种操作,如表3-3所示。

□1. x1 y1 x2 y2 v        子矩阵x1 y1 x2 y2的所有元素增加v(v>0)

□2 x1 y1 x2 y2 v         子矩阵x1 y1 x2 y2的所有元素设为v(v>0)

□3 x1 y1 x2 y2            查询子矩阵x1 y1 x2 y2的元素和、最小值和最大值。

子矩阵(x1,y1,x2,y2)是指满足x1≤x≤x2,y1≤y≤y2的所有元素(x,y)。输入保证任意时刻矩阵所有元素之和不超过10^9。

【输入格式】

对于每条类型3的操作,输出3个整数,即该子矩阵的元素和、最小值和最大值。

【分析】

矩阵不超过20行,矩阵元素却可能多达10^6个,可以想到每行建一棵线段树,则本题转化为一维问题。

本题有两个操作,add和set,因此需要两个标记addv和setv,含义同前。规定同时又两个标记时,表示先执行set在执行add。

以上内容来自算法竞赛入门经典训练指南

讲的很清楚了。。。


#include<bits/stdc++.h>
using namespace std;
const int MaxNode = 1<<17;
int _sum, _min, _max, op ,X1, X2, Y1, Y2, v;
struct IntervalTree
{
    int sumv[MaxNode], minv[MaxNode], maxv[MaxNode], addv[MaxNode], setv[MaxNode];

    //维护信息
    void maintain(int o, int L, int R)
    {
        int lc = o*2, rc = o*2+1;
        sumv[o] = minv[o] = maxv[o] = 0;
        if (R > L)
        {
            sumv[o] = sumv[lc] + sumv[rc];
            minv[o] = min(minv[lc], minv[rc]);
            maxv[o] = max(maxv[lc], maxv[rc]);
        }
        if(setv[o] >= 0) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+1); }
        if (addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+1); }
    }

    //标记传递
    void pushdown(int o)
    {
        int lc = o*2, rc = o*2+1;
        if(setv[o] >= 0)
        {
            setv[lc] = setv[rc] = setv[o];
            addv[lc] = addv[rc] = 0;
            setv[o] = -1; // 清除本结点标记
        }
        if(addv[o])
        {
            addv[lc] += addv[o];
            addv[rc] += addv[o];
            addv[o] = 0; // 清除本结点标记
        }
    }

    //更改操作
    void update(int o, int L, int R)
    {
        int lc = o*2, rc = o*2+1;
        if (Y1 <= L && R <= Y2)
        {
            if (op == 1) addv[o] += v;
            if (op == 2) { setv[o] = v; addv[o] = 0; }
        }
        else
        {
            pushdown(o);
            int M = L + (R-L)/2;
            if (Y1 <= M) update(lc, L, M);  else maintain(lc, L, M);
            if (Y2 > M) update(rc, M+1, R); else maintain(rc, M+1, R);
        }
        maintain(o, L, R);
    }

    //查询
    void query(int o, int L, int R, int add)
    {
        if (setv[o] >= 0)
        {
            int v = setv[o] + add + addv[o];
            _sum += v * (min(R, Y2)-max(L, Y1)+1);
            _min = min(_min, v);
            _max = max(_max, v);
            return ;
        }
        if (Y1 <= L && R <= Y2)
        {
            _sum += sumv[o] + add*(R-L+1);
            _min = min(_min, minv[o] + add);
            _max = max(_max, maxv[o] + add);
            return ;
        }
        int M = L + (R-L)/2;
        if (Y1 <= M) query(o*2, L, M, add + addv[o]);
        if (Y2 > M) query(o*2+1, M+1, R, add + addv[o]);
    }
};

const int MAXR = 20 + 5;
const int INF = 1000000000;

int r, c, m;
IntervalTree tree[MAXR];

int main()
{
    while (~scanf("%d%d%d", &r, &c, &m))
    {
        memset(tree, 0, sizeof(tree));
        for (int i = 1; i <= r; i++)
        {
            memset(tree[i].setv, -1, sizeof(tree[i].setv));
            tree[i].setv[1] = 0;
        }
        while (m--)
        {
            scanf("%d%d%d%d%d", &op, &X1, &Y1, &X2, &Y2);
            if (op < 3)
            {
                scanf("%d", &v);
                for (int i = X1; i <= X2; i++) tree[i].update(1, 1, c);
            }
            else
            {
                _sum = 0; _min = INF; _max = -INF;
                for (int i = X1; i <= X2; i++) tree[i].query(1, 1, c, 0);
                printf("%d %d %d\n", _sum, _min, _max);
            }
        }
    }
    return 0;
}
/*
4 4 8
1 1 2 4 4 5
3 2 1 4 4
1 1 1 3 4 2
3 1 2 4 4
3 1 1 3 4
2 2 1 4 4 2
3 1 2 4 4
1 1 1 4 3 3
*/
/*
45 0 5
78 5 7
69 2 7
39 2 7
*/



猜你喜欢

转载自blog.csdn.net/zscdst/article/details/80243314