LGP12448 [COTS 2025] 观草 Trava 学习笔记
LGP12448 [COTS 2025] 观草 Trava 学习笔记
前言
大家好啊,我是棺草袄!!!!!!!!!!!!Auch!!!!!!
今天来点大家想看的东西。
题意简述
给定一个长为 \(n\) 的正整数序列 \(A\)。有 \(m\) 个操作,形如:
? k查询 \(\sum_{i\in [1,n-k+1]}\max(a_i,a_{i+1}\dots a_{i+k-1})\)。+ k令 \(a_k\) 增加 \(1\)。
\(n,m\le 5\times 10^5\)。
做法解析
套路地考虑每个点怎么做贡献。显然你先把初始序列中每个 \(a_i\) 担任极值的范围 \((l_i,r_i)\)(是的,代码里这里求的是开区间)求出来,单调栈搞定。考虑到可能有相同元素,我们钦定更靠右的元素“更大”。
那么你考虑一下,随着 \(k\) 的改变,每个元素 \(x\) 对答案贡献的次数会怎么改变呢?答案就是所有长为 \(k\) 的区间的 \(\max\) 值的和,而 \(a_x\) 对一个这样的区间 \([a_i,a_{i+k})\) 做出贡献当且仅当 \(x\in [i,i+k)\) 且 \([a_i,a_{i+k})\in (l_i,r_i)\)。
我们称对于 \((l_i,r_i)\) 来说,\(a_i\) 往两侧延伸的长度分别为 \(i-l_i\) 和 \(r_i-i\)。我们称二者较小者为 \(d_1\),较大者为 \(d_2\)。

所以,当 \(k\in [1,d_1]\) 时,只要踩中 \(a_i\) 的区间一定都在 \((l_i,r_i)\) 范围内,所以 \(a_i\) 对答案的贡献就是 \(a_i\times k\)。而 \(k\in (d_1,d_2)\) 时,贡献次数则恒为 \(a_i\times d_1\)。而最后 \([d_2,r_i-l_i+1]\),其对答案的贡献随 \(k\) 变化呈递减的等差数列。
而修改则是简单的。我们求出修改后的 \((l_i,r_i)\)。你给这一段打个形如上文描述的增量就行。
修改那个找前后元素用能线段树二分的线段树做,维护 \(k\) 取不同值时的答案则用一棵等差数列加单点求值的线段树做。所以总共是两棵线段树,但实际上并不难写。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=5e5+5;
int N,M,A[MaxN],X;char Opt;
struct SegTree1{
int mx[MaxN<<2];
int ls(int u){return u<<1;}
int rs(int u){return (u<<1)|1;}
void pushup(int u){mx[u]=max(mx[ls(u)],mx[rs(u)]);}
void build(int u,int cl,int cr){
if(cl==cr){mx[u]=A[cl];return;}int cmid=(cl+cr)>>1;
build(ls(u),cl,cmid),build(rs(u),cmid+1,cr),pushup(u);
}
void add(int u,int cl,int cr,int dd,int x){
if(cl==cr){mx[u]+=x;return;}int cmid=(cl+cr)>>1;
dd<=cmid?add(ls(u),cl,cmid,dd,x):add(rs(u),cmid+1,cr,dd,x),pushup(u);
}
int sersuf(int u,int cl,int cr,int dl,int dr,int x){
if(mx[u]<x||dl>dr)return -1;if(cl==cr)return cl;
int cmid=(cl+cr)>>1,res=-1;
if(dl<=cl&&cr<=dr){
if(mx[rs(u)]>=x)return sersuf(rs(u),cmid+1,cr,dl,dr,x);
return sersuf(ls(u),cl,cmid,dl,dr,x);
}
if(dr>cmid)res=sersuf(rs(u),cmid+1,cr,dl,dr,x);
if(res==-1&&dl<=cmid)res=sersuf(ls(u),cl,cmid,dl,dr,x);
return res;
}
int serpre(int u,int cl,int cr,int dl,int dr,int x){
if(mx[u]<x||dl>dr)return -1;if(cl==cr)return cl;
int cmid=(cl+cr)>>1,res=-1;if(dl<=cl&&cr<=dr){
if(mx[ls(u)]>=x)return serpre(ls(u),cl,cmid,dl,dr,x);
return serpre(rs(u),cmid+1,cr,dl,dr,x);
}
if(dl<=cmid)res=serpre(ls(u),cl,cmid,dl,dr,x);
if(res==-1&&dr>cmid)res=serpre(rs(u),cmid+1,cr,dl,dr,x);
return res;
}
}SgT1;
struct SegTree2{
struct node{
lolo k,d;
friend node operator+(node &a,const node &b){return {a.k+b.k,a.d+b.d};}
friend node& operator+=(node &a,const node &b){a=a+b;return a;}
}t[MaxN<<2];
int ls(int u){return u<<1;}
int rs(int u){return (u<<1)|1;}
void add(int u,int cl,int cr,int dl,int dr,node x){
if(dl<=cl&&cr<=dr){t[u]+=x;return;}int cmid=(cl+cr)>>1;
if(dl<=cmid)add(ls(u),cl,cmid,dl,dr,x);
if(dr>cmid)add(rs(u),cmid+1,cr,dl,dr,x);
}
node query(int u,int cl,int cr,int dd){
node res=t[u];
if(cl==cr)return res;int cmid=(cl+cr)>>1;
dd<=cmid?(res+=query(ls(u),cl,cmid,dd)):(res+=query(rs(u),cmid+1,cr,dd));return res;
}
}SgT2;
int stk[MaxN],ktp,lb[MaxN],rb[MaxN];
void update(int u,int lp,int rp,lolo x){
int lg=u-lp,rg=rp-u,sg=rp-lp-1;
if(lg>rg)swap(lg,rg);
SgT2.add(1,1,N,1,lg,{x,0});
if(lg<rg)SgT2.add(1,1,N,lg+1,rg,{0,lg*x});
if(rg<sg)SgT2.add(1,1,N,rg+1,sg,{-x,(sg+1)*x});
}
lolo query(int x){
auto [k,b]=SgT2.query(1,1,N,x);
return k*x+b;
}
int main(){
readis(N,M);
for(int i=1;i<=N;i++){
readi(A[i]);int &k=ktp;
while(k&&A[stk[k]]<=A[i])rb[stk[k]]=i,k--;
lb[i]=stk[k],stk[++k]=i;
}
while(ktp)rb[stk[ktp--]]=N+1;
for(int i=1;i<=N;i++)update(i,lb[i],rb[i],A[i]);
SgT1.build(1,1,N);
int cnt=0;
for(int i=1;i<=M;i++){
scanf(" %c",&Opt);readi(X);
if(Opt=='?')writil(query(X));
else{
A[X]++,SgT1.add(1,1,N,X,1);
int cl=SgT1.sersuf(1,1,N,1,X-1,A[X]);if(cl==-1)cl=0;
int cr=SgT1.serpre(1,1,N,X+1,N,A[X]);if(cr==-1)cr=N+1;
update(X,cl,cr,1);
}
}
return 0;
}
后记
两棵线段树又咋样?照样击而破之!别把问题想太难。
(然而实际上我调了一个半小时,惨)
浙公网安备 33010602011771号