ABC342

G

link

总之纪念一下赛时G。

经典标记永久化,可惜我之前不会标记永久化,于是在赛时自己推导了(发明了)一种算法,赛后才知道是标记永久化。

建一棵线段树,考虑没有撤销操作的情况。

显然,一次操作代表一个区间所有数不能小于 \(x\),于是我们将线段树上包含在区间内的节点标记上 \(x\),如果之前已经有标记,就与之前标记取较大值。

查询时由于是单点查询,可以将路径上的标记取最大值与 \(A_i\) 比较即为答案。

正确性显然。

考虑撤销操作。本质上就是废除一次标记。

于是我们考虑将每个节点的历史标记记录下来,撤销时删除这次历史即可,用 multiset 可以方便维护历史标记最大值。

由于每次修改操作只会改变 \(\log\) 个节点,于是一次撤销也只对这 \(\log\) 个节点操作,时间复杂度 \(O(q\log^2 n)\),可能常数略大。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,I b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxn=2e5+10;

int a[maxn];

struct node{
	int l,r,ls,rs;
	std::multiset<int>st;
}s[maxn*2];

struct op{
	int k,x;
};

std::vector<op>po[maxn];

int tot=0,cnt=0;

int build(int l,int r){
	int p=++tot;
	s[p].l=l,s[p].r=r;
	s[p].st.insert(-inf);
	if(l==r) return p;
	int mid=(l+r)>>1;
	s[p].ls=build(l,mid);
	s[p].rs=build(mid+1,r);
	return p;
}

void update(int k,int p,int L,int R,int x){
	int l=s[p].l,r=s[p].r;
	if(l>=L&&r<=R){
		s[p].st.insert(x);
		op nowop;
		nowop.k=p,nowop.x=x;
		po[k].push_back(nowop);
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=L) update(k,s[p].ls,L,R,x);
	if(R>mid) update(k,s[p].rs,L,R,x);
	return ;
}

void del(int k){
	//撤销第 k 次插入操作
	for(auto q:po[k]){
		s[q.k].st.erase(s[q.k].st.find(q.x));
	} 
}

int query(int p,int k){
	int l=s[p].l,r=s[p].r;
	auto z=s[p].st.end();
	z--;
	int ret=*z;
	if(l==r) return ret;
	int mid=(l+r)>>1;
	if(k<=mid) chkmax(ret,query(s[p].ls,k));
	else chkmax(ret,query(s[p].rs,k));
	return ret;
}

int t[maxn];//t[i] 代表第 i 次操作是第几次插入操作 

signed main(){
	int n;
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,n);
	int q;
	read(q);
	int tim=0;
	while(q--){
		tim++;
		int opt;
		read(opt);
		if(opt==1){
			int l,r,x;
			read(l),read(r),read(x);
			cnt++;
			t[tim]=cnt;
			update(cnt,1,l,r,x);
		}else if(opt==2){
			int i;
			read(i);
			del(t[i]);
		}else{
			int i;
			read(i);
			printf("%lld\n",std::max(query(1,i),a[i]));
		}
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
posted @ 2024-02-25 01:20  BYR_KKK  阅读(23)  评论(0编辑  收藏  举报