UOJ是真的引领时代潮流。
首先显然有一个线段树维护区间单调栈的方法,但是是\(O(m\log ^2n)\)的并不够优秀。因为我们不需要知道区间的信息,我们只需要知道后缀的信息。
考虑离线,按照序列顺序从后往前维护时间轴,每次相当于区间取\(\min\),以及单点询问被真正取\(\min\)的次数。这个可以用一个Segment Beats解决。
什么,你不会Segment Beats?
这个是一种维护区间取\(\min\)的数据结构,具体的,你需要在线段树上每个区间维护两个值,\(mx\)表示最大值,\(se\)表示次大值。每次修改先把线段树上对应\(\log n\)个区间抠出来,然后分类讨论:
若当前取\(\min\)的值\(z\geq mx\),则这次修改对这个区间没有影响,可以直接返回。
若\(se<z<mx\),则打上标记,表示将最大值改为\(z\),并将修改次数加一。
若\(z\leq se\),则继续递归。
可以用势能分析证明复杂度为\(O(n+m\log n)\),记\(g_x\)为线段树上\(x\)节点对应的子树内的不同种类的数的数量,则每次继续递归会使区间内不同的值得数量至少减一,而不完全包含的节点则会至多\(+1\),总势能增加量不超过\(O(m\log n)\),因此复杂度\(O(n+m\log n)\)
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=1e6+5,M=N*4+5,K=2e3+5,mod=998244353,Mod=mod-1;const db eps=1e-5;const int INF=1e9+7;
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
int n,m,A[N],Op[N],X[N],y,Ans[N];vector<int> D[N],Q[N];
namespace SB{
#define ls v<<1
#define rs v<<1|1
int Mx[M],Se[M],Y[M];void PF(int v,int x,int y){x<Mx[v]&&(Mx[v]=x,Y[v]+=y);}void P(int v){Y[v]&&(PF(ls,Mx[v],Y[v]),PF(rs,Mx[v],Y[v]),Y[v]=0);}
void Up(int v){Mx[v]=max(Mx[ls],Mx[rs]);Se[v]=-INF;min(Mx[ls],Mx[rs])^Mx[v]&&(Se[v]=min(Mx[ls],Mx[rs]));Se[v]=max(Se[v],max(Se[ls],Se[rs]));}
void BD(int l=0,int r=m,int v=1){Mx[v]=INF;Se[v]=-INF;if(l==r)return;int m=l+r>>1;BD(l,m,ls);BD(m+1,r,rs);}
void Ins(int x,int y,int z,int l=0,int r=m,int v=1){if(Mx[v]<=z) return;if(x<=l&&r<=y&&z>Se[v]) return PF(v,z,1);int m=l+r>>1;P(v);x<=m&&(Ins(x,y,z,l,m,ls),0);y>m&&(Ins(x,y,z,m+1,r,rs),0);Up(v);}
int Qry(int x,int l=0,int r=m,int v=1){if(l==r) return Y[v];int m=l+r>>1;P(v);return x<=m?Qry(x,l,m,ls):Qry(x,m+1,r,rs);}
void print(int l=0,int r=m,int v=1){if(l==r){printf("%d %d %d\n",l,Mx[v],Y[v]);return;}int m=l+r>>1;P(v);print(l,m,ls);print(m+1,r,rs);}
#undef ls
#undef rs
}
int main(){
freopen("1.in","r",stdin);
int i,j;fin>>n>>m;for(i=1;i<=n;i++) fin>>A[i];for(i=1;i<=m;i++) fin>>Op[i]>>y,Op[i]^2?fin>>X[i],D[y].PB(i):Q[y].PB(i);
SB::BD();for(i=n;i;i--){X[0]=A[i];y=0;for(int j:D[i]) SB::Ins(y,j-1,X[y]),y=j;SB::Ins(y,m,X[y]);for(int j:Q[i]) Ans[j]=SB::Qry(j);/*printf("%d\n",i);SB::print();Pc('\n');*/}for(i=1;i<=m;i++) Op[i]^1&&(fout<<Ans[i]<<'\n',0);
}
标签:SB,ch,log,19,int,using,UOJ,515,define
From: https://www.cnblogs.com/275307894a/p/16632706.html