记一下这种有趣的 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;
}
标签:CF1093G,int,题解,mid,st,绝对值,const,Multidimensional,ql
From: https://www.cnblogs.com/lingspace/p/cf1093g.html