记一下这种有趣的 trick.
思路
线段树。
绝对值按照惯例是可以拆的,并且可以拆出一正一负两个数。
考虑到维数很小,可以考虑状压表示拆除绝对值之后每一维值的正负。
并且因为绝对值中每个值都是非负的,不正确的拆除方案会导致其中的一些绝对值变成负数,所以最终的结果一定是每种正负组合中权值最大的。
令 k′ 为 k 二进制上每一位反转过后得到的数,f(k) 表示状态 k 对应的权值和,则最终的结果为 maxk≤2m−1f(k)+f(k′).
现在的问题是如何求 f,考虑到询问的是一个连续的区间,可以试着用线段树做。
单点直接按照 k 求和就行。
对于两个子区间的合并,显然直接对每一种状态的权值取最大值就行。
合并的复杂度是 O(2k),所以总复杂度是 O(nlogn2k).
代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现