【UOJ515】【UR #19】前进四(SegmentTree Beats)
- 给定一个长度为 \(n\) 的序列。
- \(q\) 次操作,分为两种:单点修改;询问 \(a_{x\sim n}\) 中有多少种不同的后缀最小值。
- \(1\le n,q\le10^6\)
退役后的第一篇题解。因为马上下课了所以写得有点急。
算是自己做出来的,一开始被人带偏了思路导致浪费不少时间 /fn
离线转化+SegmentTree Beats
因为每次针对一段后缀询问,所以考虑从后往前加入每个位置上的元素,去处理它对不同版本的数组的影响。
我们对于每个版本的数组记录下已有的后缀最小值。
当前位置上的元素的取值可以被划分为若干段,对于每段需要去将一段区间内所有版本的后缀最小值向某个值取 \(\min\),且我们需要记录下每个版本后缀最小值的被修改次数。
容易想到 SegmentTree Beats。由于 SegmentTree Beats 上每次打标记含义都是将范围内所有最小值修改成统一个值,所以修改次数顺带用一个标记记录一下即可。
代码:\(O(q\log n)\)
#include<bits/stdc++.h>
#define Cn const
#define CI Cn int&
#define N 1000000
using namespace std;
namespace FastIO
{
#define FS 100000
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}struct IO_Cl {~IO_Cl() {clear();}}CL;
Tp void read(Ty& x) {x=0;while(!isdigit(oc=tc()));while(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp void writeln(Ty x) {while(OS[++OT]=x%10+48,x/=10);while(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int n,Qt,ct,a[N+5],ti[N+5],ans[N+5];
struct Data {int x,v,l,r;bool operator < (Cn Data& o) Cn {return x>o.x;}}s[2*N+5];
struct Qry {int p,x;bool operator < (Cn Qry& o) Cn {return x>o.x;}}q[N+5];
class SegmentTree//SegmentTree Beats
{
private:
#define PT int l=1,int r=n,int o=1
#define LT l,u,o<<1
#define RT u+1,r,o<<1|1
#define PD(o) (F[o]!=1e9&&(T(o<<1,F[o],G[o]),T(o<<1|1,F[o],G[o]),F[o]=1e9,G[o]=0))
#define T(o,v,u) (Mx[o]>v&&(Mx[o]=v,F[o]=min(F[o],v),G[o]+=u))
int Mx[N<<2],Sx[N<<2],F[N<<2],G[N<<2];
void PU(int o)
{
if(Mx[o<<1]==Mx[o<<1|1]) Mx[o]=Mx[o<<1],Sx[o]=max(Sx[o<<1],Sx[o<<1|1]);
else Mx[o<<1]>Mx[o<<1|1]?(Mx[o]=Mx[o<<1],Sx[o]=max(Sx[o<<1],Mx[o<<1|1])):(Mx[o]=Mx[o<<1|1],Sx[o]=max(Sx[o<<1|1],Mx[o<<1]));
}
public:
void Bd(PT) {if(Mx[o]=F[o]=1e9,l==r) return;int u=l+r>>1;Bd(LT),Bd(RT);}
void U(int L,int R,int v,PT)//区间向v取min
{
if(Mx[o]<=v) return;if(L<=l&&r<=R&&Sx[o]<v) return (void)T(o,v,1);
int u=l+r>>1;PD(o),L<=u&&(U(L,R,v,LT),0),R>u&&(U(L,R,v,RT),0),PU(o);
}
int Q(int x,PT)//单点询问被修改次数
{
if(l==r) return G[o];int u=l+r>>1;PD(o);return x<=u?Q(x,LT):Q(x,RT);
}
}S;
int main()
{
int i;for(read(n,Qt),i=1;i<=n;++i) read(a[i]),ti[i]=1;
int op,x,y,nw=1;for(i=1;i<=Qt;++i) if(read(op),op==2) q[nw].p=nw,read(q[nw++].x);
else read(x),ti[x]^nw&&(s[++ct]=(Data){x,a[x],ti[x],nw-1},ti[x]=nw),read(a[x]);
for(i=1;i<=n;++i) ti[i]^nw&&(s[++ct]=(Data){i,a[i],ti[i],nw-1},0);
int p1=1,p2=1;for(sort(s+1,s+ct+1),sort(q+1,q+nw),S.Bd(),i=n;i;--i)//从后往前加入每个位置上的元素
{while(p1<=ct&&s[p1].x==i) S.U(s[p1].l,s[p1].r,s[p1].v),++p1;while(p2^nw&&q[p2].x==i) ans[q[p2].p]=S.Q(q[p2].p),++p2;}
for(i=1;i^nw;++i) writeln(ans[i]);return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒