Codeforces 1093G (线段树 + 拆绝对值式子 + k维曼哈顿距离)

Codeforces 1093G
题意):给你 $n$ 个 $k$ 维的点 $a_{1..n}$,定义两点$(x_1,x_2,\cdots,x_k),(y_1,y_2,\cdots,y_k)$间的曼哈顿距离为 $\sum_{i=1}^k|x_i-y_i|$。
你需要执行下面两种操作:
$1 i b_1 b_2\cdots b_k$,表示将 $a_i$修改为 $(b_1,b_2,\cdots,b_k)$。
$2 l r$,表示询问 $[l,r]$ 内最大的两点间曼哈顿距离,即任取 $x,y\in[l,r]$ 得到的所有曼哈顿距离中的最大值。
翻译来自Luogu

对于绝对值式子,不好处理我们作一个变式。
考虑二维的情况,设点$(a_1, a_2), (b_1, b_2)$,则根据绝对值函数定义和$x \leq |x|$,有
$$
|a_1-b_1|+|a_2-b_2| \\
= \max (a_1-b_1, b_1-a_1)+ \max (a_2-b_2, b_2-a_2) \\
= \max (a_1-b_1+a_2-b_2, a_1-b_1+b_2-a_2, b_1-a_1+a_2-b_2, b_1-a_1+b_2-a_2) \\
= \max ((a_1+a_2)-(b_1+b_2), (a_1-a_2)-(b_1-b_2), (-a_1+a_2)-(-b_1+b_2), (-a_1-a_2)-(-b_1-b_2))
$$

观察到符号是+-的$2^d$种不同组合,所以我们设第$i$个点的第$j$种为$s(i,j)$,那么两点曼哈顿距离最大值就是
$$
\max_j \max_i(s(i,j)-s(i,j))
$$
变形得
$$
\max_j (\max_i(s(i,j))-\min_i(s(i,j)))
$$

那么用线段树维护$\max_is(i,j)$和$\min_is(i,j)$即可。

知识点:
以后遇到绝对值$\max \min$式子要尝试拆一下,因为很多这样的式子是不方便直接处理的,要转化一下。

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    const int MAXN = 200000 + 5;
    const LL INF = 3223372036854775808ll;

    int n, k, q, inp[10];
    LL s[40], retmax[40], retmin[40];

    #define M ((l + r) >> 1)
    #define lc (o << 1)
    #define rc (o << 1 | 1)
    LL maxv[MAXN * 4][40], minv[MAXN * 4][40];

    void pushup(int o) {
        for (int i = 0; i <= 32; ++i) maxv[o][i] = max(maxv[lc][i], maxv[rc][i]);
        for (int i = 0; i <= 32; ++i) minv[o][i] = min(minv[lc][i], minv[rc][i]);
    }
    void build(int o, int l, int r) {
        if (l == r) {
            for (int i = 0; i <= 32; ++i) maxv[o][i] = -INF;
            for (int i = 0; i <= 32; ++i) minv[o][i] = INF;
        } else {
            build(lc, l, M), build(rc, M + 1, r);
            pushup(o);
        }
    }
    void update(int o, int l, int r, int p) {
        if (l == r) {
            for (int i = 0; i <= 32; ++i) maxv[o][i] = s[i];
            for (int i = 0; i <= 32; ++i) minv[o][i] = s[i];
            return ;
        }
        if (p <= M) update(lc, l, M, p);
        else if (M < p) update(rc, M + 1, r, p);
        pushup(o);
    }
    void query(int o, int l, int r, int x, int y) {
        if (x <= l && r <= y) {
            for (int i = 0; i <= 32; ++i) retmax[i] = max(retmax[i], maxv[o][i]);
            for (int i = 0; i <= 32; ++i) retmin[i] = min(retmin[i], minv[o][i]);
            return ;
        }
        if (x <= M) query(lc, l, M, x, y);
        if (M < y) query(rc, M + 1, r, x, y);
    }

    void clean() {
    }
    int solve() {

        clean();
        scanf("%d%d", &n, &k);
        build(1, 1, n);

        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= k; ++j) scanf("%d", &inp[j]);
            ms(s, 0);
            for (int S = 0; S < (1 << k); ++S) {
                for (int j = 1; j <= k; ++j) {
                    s[S] += ((S & (1 << (j - 1))) ? 1ll : -1ll) * inp[j];
                }
            }
            update(1, 1, n, i);
        } 

        scanf("%d", &q);
        while (q--) {
            int tp; scanf("%d", &tp);
            if (tp == 1) {
                int x; scanf("%d", &x);
                for (int i = 1; i <= k; ++i) scanf("%d", &inp[i]);
                ms(s, 0);
                for (int S = 0; S < (1 << k); ++S) {
                    for (int i = 1; i <= k; ++i) {
                        s[S] += ((S & (1 << (i - 1))) ? 1ll : -1ll) * inp[i];
                    }
                }
                update(1, 1, n, x);
            } 
            if (tp == 2) {
                int l, r; scanf("%d%d", &l, &r);
                for (int S = 0; S < (1 << k); ++S) retmax[S] = -INF, retmin[S] = INF;
                query(1, 1, n, l, r);
                LL ans = 0;
                for (int i = 1; i < (1 << k); ++i) ans = max(ans, retmax[i] - retmin[i]);
                printf("%lld\n", ans);
            }
        }

        return 0;
    }
}
int main() {
    flyinthesky::solve();
    return 0;
}
------ 本文结束 ------