LGP12448 [COTS 2025] 观草 Trava 学习笔记

LGP12448 [COTS 2025] 观草 Trava 学习笔记

Luogu Link

前言

大家好啊,我是棺草袄!!!!!!!!!!!!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\)

pVMIcxP.png

所以,当 \(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;
}

后记

两棵线段树又咋样?照样击而破之!别把问题想太难。

(然而实际上我调了一个半小时,惨)

posted @ 2025-07-07 12:52  矞龙OrinLoong  阅读(14)  评论(0)    收藏  举报