cf739 C. Alyona and towers(线段树)
题意:
给定数组,要求实现两种操作:区间加、查询数组中最长的 “先严格上升再严格下降” 的连续子区间的长度。
注意只严格上升或只严格下降或只有一个元素也是合法的。
思路:
把长为n的原数组处理成相邻项的差,长为n-1。w[i]表示第i+1个数与第i个数的差。
考虑相邻项的差的符号,1,0,-1表示大于零,等于零,小于零。满足条件的形式为 \(+1,+1,+1,-1,-1\) ,即差的符号函数值非严格递减,不能出现0。
用线段树维护差的符号函数值,记录区间最大的连续非严格递减子列的长度、从左端点开始的长度和从右端点开始的长度。合并区间时要判断左右儿子相接的地方能不能合并。
因为差分了所以只需单点修改。查询时直接输出根节点上的答案。另外再实现个建树函数和pushup即可,不需要pushdown。pushup和main函数中有挺多细节,其他几个函数跟普通线段树差不多。
对于每次修改,首先直接修改差分数组(即原数组),如果改变了差的符号才需要改线段树。
n=1时根本没有相邻项,特判一下。
#include
using namespace std;
using ll = long long;
const int N = 3e5 + 5;
ll w[N];
int sgn(ll x) { //返回x的符号
if(x > 0) return 1; if(x < 0) return -1;
return 0;
}
struct node {
int l, r, len, llen, rlen;
} tr[N*4];
void pushup(int u)
{
tr[u].len = max(tr[u<<1].len, tr[u<<1|1].len);
tr[u].llen = tr[u<<1].llen, tr[u].rlen = tr[u<<1|1].rlen;
if(w[tr[u<<1].r] && w[tr[u<<1|1].l] && //不能是0
sgn(w[tr[u<<1].r]) >= sgn(w[tr[u<<1|1].l])) //且须递减
{
tr[u].len = max(tr[u].len, tr[u<<1].rlen + tr[u<<1|1].llen);
if(tr[u<<1].llen == tr[u<<1].r - tr[u<<1].l + 1)
tr[u].llen += tr[u<<1|1].llen;
if(tr[u<<1|1].rlen == tr[u<<1|1].r - tr[u<<1|1].l + 1)
tr[u].rlen += tr[u<<1].rlen;
}
}
void build(int u, int l, int r)
{
if(l == r) //只维护符号
{
bool tmp = w[l];
tr[u] = {l, r, tmp, tmp, tmp};
}
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
void modify(int u, int p, int x) //把单点p改为x
{
if(tr[u].l == tr[u].r)
{
bool tmp = x;
tr[u] = {tr[u].l, tr[u].r, tmp, tmp, tmp};
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if(p <= mid) modify(u<<1, p, x);
else modify(u<<1|1, p, x);
pushup(u);
}
signed main()
{
int n, q; scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", &w[i]);
if(n == 1) {
scanf("%d", &q); while(q--) puts("1");
return 0;
}
for(int i = 1; i < n; i++) w[i] = w[i+1] - w[i]; //差分
build(1, 1, n-1); //维护n-1个相邻差
scanf("%d", &q); while(q--)
{
int l, r, d; scanf("%d%d%d", &l, &r, &d);
//pushup里面要用新的w[i],所以必须先更新w再修改
if(l > 1) {
if(sgn(w[l-1]) != sgn(w[l-1]+d))
w[l-1] += d, modify(1, l-1, sgn(w[l-1]));
else w[l-1] += d;
}
if(r < n) {
if(sgn(w[r]) != sgn(w[r]-d))
w[r] -= d, modify(1, r, sgn(w[r]));
else w[r] -= d;
}
printf("%d\n", tr[1].len + 1);
}
return 0;
}