Poster / 海报
题目链接:jzoj 7186 / LOJ 3264
题目大意
给你一个环,环上每个点有权值。
然后你可以选一些点,保证不能有超过四个在一起的点都被选到,问你能选的出的点的权值和的最大值。
然后会修改一些点的权值,每修改一次,都要重新求最大值。
思路
看到有修改,我们考虑用数据结构,然后就考虑用线段树。
根据不能有四个连在一起的都被选,我们搞一个东西,是保证中间合法,最后又两边已经连续选了多少个的东西给线段树。(
n
u
m
i
,
j
num_{i,j}
numi,j)
不难看到它是
4
∗
4
4*4
4∗4 的数组,然后你考虑假设求出来了,怎么求答案。
因为是一个环,那你还要保证连接的头尾也合法,那就枚举
i
,
j
i,j
i,j 使得
i
+
j
⩽
3
i+j\leqslant3
i+j⩽3,然后这些答案取最大值即可。
然后你考虑如何合并。
首先是普通的情况,你就考虑左边的右边连续,右边的左边连续,然后合再一起。
但是你会发现一个问题,就是可能它有一边整块都选中,那新的左边连续跟右边连续就不是那样的了。
那如果左边整块选中,是
j
j
j 个,那你右边左边是
i
i
i,那你左边的转移就是
n
u
m
j
,
j
num_{j,j}
numj,j,得到的左边就是
j
+
i
j+i
j+i,右边也同理。
(当然也有两个都是整块选中,那就两个都这样搞就可以了)
然后有一个就是合并普通的合并用 4 4 4^4 44 会爆,你可以先枚举最左最右,然后中间的枚举一个,然后让另一个可以选的增加的过程中类似前缀记录下来,就可以优化到 4 3 4^3 43,就可以过了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
struct node {
int l, r, nm;
LL num[4][4];
}t[160001];
int n, q, x;
LL a[40001];
void up(int now) {
memset(t[now].num, 0, sizeof(t[now].num));
t[now].nm = t[now << 1].nm + t[now << 1 | 1].nm;
int l = (now << 1), r = (now << 1 | 1);
for (int i = 0; i <= min(3, t[l].nm); i++)
for (int j = 0; j <= min(3, t[r].nm); j++) {
if (i == t[l].nm && j == t[r].nm) {//都头尾碰到了
if (i + j <= 3) t[now].num[i + j][i + j] = max(t[now].num[i + j][i + j], t[l].num[i][i] + t[r].num[j][j]);
continue;
}
if (i == t[l].nm) {//有一个头尾碰到了
for (int k = 0; i + k <= 3 && k <= t[r].nm; k++)
t[now].num[i + k][j] = max(t[now].num[i + k][j], t[l].num[i][i] + t[r].num[k][j]);
continue;
}
if (j == t[r].nm) {
for (int k = 0; k + j <= 3 && k <= t[l].nm; k++)
t[now].num[i][k + j] = max(t[now].num[i][k + j], t[l].num[i][k] + t[r].num[j][j]);
continue;
}
LL sum = 0;
for (int k = 3; k >= 0; k--) {
sum = max(sum, t[r].num[3 - k][j]);//优化掉另一个枚举不然会超时
t[now].num[i][j] = max(t[now].num[i][j], t[l].num[i][k] + sum);
}
}
}
void build(int now, int l, int r) {
t[now].l = l; t[now].r = r;
if (l == r) {
t[now].nm = 1;
t[now].num[1][1] = a[l];
return ;
}
int mid = (l + r) >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
up(now);
}
void insert(int now, int l, int r, int pl, LL val) {
if (l == r) {
t[now].num[1][1] = val;
return ;
}
int mid = (l + r) >> 1;
if (pl <= mid) insert(now << 1, l, mid, pl, val);
else insert(now << 1 | 1, mid + 1, r, pl, val);
up(now);
}
LL get_top() {
LL re = 0;
for (int i = 0; i <= 3; i++)
for (int j = 0; i + j <= 3; j++)//限制是环的条件,两边加起来也不可以大于 3
re = max(re, t[1].num[i][j]);
return re;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, 1, n);
scanf("%d", &q);
printf("%lld\n", get_top());
while (q--) {
scanf("%d", &x);
scanf("%lld", &a[x]);
insert(1, 1, n, x, a[x]);
printf("%lld\n", get_top());
}
return 0;
}