Loading

【题解】CF1093G Multidimensional Queries

记一下这种有趣的 trick.

思路

线段树。

绝对值按照惯例是可以拆的,并且可以拆出一正一负两个数。

考虑到维数很小,可以考虑状压表示拆除绝对值之后每一维值的正负。

并且因为绝对值中每个值都是非负的,不正确的拆除方案会导致其中的一些绝对值变成负数,所以最终的结果一定是每种正负组合中权值最大的。

\(k^{\prime}\)\(k\) 二进制上每一位反转过后得到的数,\(f(k)\) 表示状态 \(k\) 对应的权值和,则最终的结果为 \(\max\limits_{k \leq 2^m - 1} f(k) + f(k^{\prime})\).

现在的问题是如何求 \(f\),考虑到询问的是一个连续的区间,可以试着用线段树做。

单点直接按照 \(k\) 求和就行。

对于两个子区间的合并,显然直接对每一种状态的权值取最大值就行。

合并的复杂度是 \(O(2^k)\),所以总复杂度是 \(O(n \log n 2^k)\).

代码

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 2e5 + 5;
const int maxk = 6;
const int st_sz = (1 << maxk);
const int sgt_sz = maxn << 2;
const int inf = 0x3f3f3f3f;

int n, m, q;
int qry_st[st_sz], tmp_st[maxk];
int a[maxn][maxk];

inline int max(const int &a, const int &b) { return (a >= b ? a : b); }

namespace SGT
{
    #define ls (k << 1)
    #define rs (k << 1 | 1)

    int st[sgt_sz][st_sz];

    void push_up(int k) { for (int i = 0; i < (1 << m); i++) st[k][i] = max(st[ls][i], st[rs][i]); }

    void build(int k, int l, int r)
    {
        if (l == r)
        {
            for (int i = 0; i < (1 << m); i++)
                for (int j = 1; j <= m; j++)
                    st[k][i] += (((i >> (j - 1)) & 1) ? 1 : -1) * a[l][j];
            return;
        }
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r), push_up(k);
    }

    void update(int k, int l, int r, int p)
    {
        if (l == r)
        {
            memset(st[k], 0, (1 << m) * sizeof(int));
            for (int i = 0; i < (1 << m); i++)
                for (int j = 1; j <= m; j++)
                    st[k][i] += (((i >> (j - 1)) & 1) ? 1 : -1) * tmp_st[j];
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) update(ls, l, mid, p);
        else update(rs, mid + 1, r, p);
        push_up(k);
    }

    void query(int k, int l, int r, int ql, int qr)
    {
        if ((l >= ql) && (r <= qr))
        {
            for (int i = 0; i < (1 << m); i++) qry_st[i] = max(qry_st[i], st[k][i]);
            return;
        }
        int mid = (l + r) >> 1;
        if (ql <= mid) query(ls, l, mid, ql, qr);
        if (qr > mid) query(rs, mid + 1, r, ql, qr);
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &a[i][j]);
    SGT::build(1, 1, n);
    scanf("%d", &q);
    while (q--)
    {
        int opt;
        scanf("%d", &opt);
        if (opt == 1)
        {
            int p;
            scanf("%d", &p);
            for (int i = 1; i <= m; i++) scanf("%d", &tmp_st[i]);
            SGT::update(1, 1, n, p);
        }
        else
        {
            int l, r, ans = 0;
            scanf("%d%d", &l, &r);
            for (int i = 0; i < (1 << m); i++) qry_st[i] = -inf;
            SGT::query(1, 1, n, l, r);
            for (int i = 0; i < (1 << m); i++) ans = max(ans, qry_st[i] + qry_st[((1 << m) - 1) ^ i]);
            printf("%d\n", ans);
        }
    }
    return 0;
}
posted @ 2023-02-09 16:42  kymru  阅读(10)  评论(0编辑  收藏  举报