把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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;
}
posted @ 2022-09-15 22:08  TheLostWeak  阅读(233)  评论(0编辑  收藏  举报