[BZOJ4826] [HNOI2017] 影魔 单调栈 主席树

本文详细解析了一道算法竞赛题目,该题涉及区间最大值和次大值的查询,通过使用单调栈和主席树的数据结构,高效地解决了问题。文章深入探讨了算法思路,包括如何选取区间代表值,以及如何利用数据结构进行快速查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

因为是一个排列,所以不会有重复的。如果有重复就没法做了。一开始没有仔细看题目想了半天。

发现,如果是第一种情况,那么边界\(l\)\(r\)就应该分别是整个区间的最大值和次大值。

然后,对于那第二种情况, \(l\)\(r\)中,只有一个数是最大值,另一个数不可以是最大值和次大值。

于是我们考虑从每一个合法区间内数里面选出一个代表来可以直接代表整个区间。

用单调栈维护一下\(lp[i]\)\(rp[i]\)分别表示一个数左边和右边离\(i\)最近的大于之的数。

然后对于第一种情况,发现相邻两个数一定是对的。然后发现,任何一个\([lp[i],rp[i]]\)也是对的。因为可以保证边界上的数的最大与次大的性质。

那么第二种呢?只要保证一个边界最大即可,因此就是\([lp[i]+1..i-1,rp[i]]\)\([lp[i],i+1..rp[i]-1]\)。可以保证不会重复,因为这里\(i\)实际上是每个区间的次大值,每个区间只会被其次大值统计一次。

于是我们考虑把一个区间的边界位置分别作为二维的坐标,发现问题就变成了求以\(l,l\)\(r,r\)为顶点的正方形内的和。

显然可以用扫描线。但是其实可以更方便。每一个位置只能是一个线段,所以我们直接用主席树维护一下即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define REP(i,a,n) for(register int i(a);i<=(n);++i)
#define PER(i,a,n) for(register int i(a);i>=(n);--i)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
const int SZ=(1<<21)+1;char ibuf[SZ],*iS,*iT,obuf[SZ+128],*oS=obuf,*oT=obuf+SZ-1;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SZ,stdin),iS==iT?EOF:*iS++):*iS++)
#endif
template<typename I>inline void read(I&x){char c=gc();int f=1;for(;c<'0'||c>'9';c=gc())c=='-'?f=-1:0;for(x=0;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c&15);f==-1?x=-x:0;}
inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
#define printf(...) oS>oT&&(flush(),1),oS+=sprintf(oS,__VA_ARGS__)
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline char SMAX(A&a,const B&b){return a<b?a=b,1:0;}
template<typename A,typename B>inline char SMIN(A&a,const B&b){return a>b?a=b,1:0;}

const int N=200000+7;
int n,m,T,p1,p2,a[N],x,y,q[N],tl,lp[N],rp[N];

struct Node{int lc,rc,lps;ll val,add;}t[N*50];int RT[N],nod;//错误笔记:这里不是单调修改,所以仅仅开LOG被是不够的 
inline void Insert(int &o,int L,int R,int l,int r,int k){
    if(t[o].lps<T)t[++nod]=t[o],o=nod,t[o].lps=T;
    if(l<=L&&R<=r)return (void)(t[o].add+=k,t[o].val+=(ll)k*(R-L+1));
    int M=(L+R)>>1;if(l<=M)Insert(t[o].lc,L,M,l,r,k);if(r>M)Insert(t[o].rc,M+1,R,l,r,k);
    t[o].val=t[t[o].lc].val+t[t[o].rc].val+t[o].add*(R-L+1);
}
inline ll Query(int o,int p,int L,int R,int l,int r,ll adv){
    if(l<=L&&R<=r)return t[o].val-t[p].val+adv*(R-L+1);
    int M=(L+R)>>1;if(r<=M)return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add);else if(l>M)return Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
    else return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add)+Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
}

struct Pair{int x,y,w;};std::vector<Pair>g[N];
inline void Preprocess(){
    REP(i,1,n){
        while(tl&&a[q[tl]]<a[i])--tl;
        q[++tl]=i;lp[i]=q[tl-1];
    }q[tl=0]=n+1;PER(i,n,1){
        while(tl&&a[q[tl]]<a[i])--tl;
        q[++tl]=i;rp[i]=q[tl-1];
        if(i<n)g[i].push_back(Pair{i+1,i+1,p1});
        if(lp[i]&&rp[i]<=n)g[rp[i]].push_back(Pair{lp[i],lp[i],p1});
        if(rp[i]<=n&&i-1>=lp[i]+1)g[rp[i]].push_back(Pair{lp[i]+1,i-1,p2});
        if(lp[i]&&i+1<=rp[i]-1)g[lp[i]].push_back(Pair{i+1,rp[i]-1,p2});
    }
    REP(i,1,n){
        T=i;RT[i]=RT[i-1];int len=g[i].size();
        REP(j,0,len-1)Insert(RT[i],1,n,g[i][j].x,g[i][j].y,g[i][j].w);
    }
}

int main(){
    read(n);read(m),read(p1),read(p2);
    REP(i,1,n)read(a[i]);
    Preprocess();
    REP(i,1,m){
        read(x),read(y);
        printf("%lld\n",Query(RT[y],RT[x-1],1,n,x,y,0));
    }return flush(),0;
}

转载于:https://www.cnblogs.com/hankeke/p/9971234.html

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值