ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「CF803G Periodic RMQ Problem」

2020-04-16 19:51:14  阅读:248  来源: 互联网

标签:now RMQ min int cover sgt Periodic right CF803G


题目大意

给出一个长度为 \(n\) 的序列 \(b\),序列 \(a\) 又序列 \(b\) 复制 \(k\) 次后得到,需要支持区间覆盖,区间取 \(min\).

分析

开始没有想到可以用st表,很自闭.

但是毕竟写出来了,就来分享一个不用st表的做法,这个做法可能比较复杂,如果想要实现更方便的做法可以看看\(\color{red}{\sf{M}}\color{black}{\sf{eatherm}}\)神仙的题解.

可以发现如果暴力建树的复杂度是 \(\mathcal{O}(nk\log_2nk)\) 显然会 \(T\),开始的时候每连续 \(n\) 个都是相同的,所以考虑建 \(k\) 颗树,每颗树就是一个块,开始时都和初始树(用 \(b\) 建成的树)相同.

当然这里需要动态开点.

然后是修改操作,考虑将修改部分变成三段,分别为左端点所在的块,右端点所在的块,中间的块,然后对于每一段进行修改.

  1. 对于左右的块,直接在左右块对于的线段树上修改就可以了
  2. 对于中间的块,显然不可以暴力修改,那么考虑再用一颗线段树维护

然后是查询操作,仍然将它分成三段来查询,和修改的方式差不多.

但是在查询左右块时需要查询左右快是否被覆盖,如果被覆盖那么在维护所有块的线段树中将这个标记去掉,再进行全局覆盖.

时间复杂度 \(\mathcal{O}(m(log_2n+log_2k))\).

这就是线段树优化分块吗,i了i了.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=1e5+7;
const int INF=1e9;
int n,m,k;
int arr[MAXN];
int root[MAXN];
int min_num=INF;
namespace All//维护块的线段树
{
	struct LazyTag//有覆盖标记
	{
		int if_cover;
		int cover;
		void Clean()
		{
			if_cover=0;
		}
	}for_make;
	LazyTag MakeTag(int cover)
	{
		for_make.if_cover=1;
		for_make.cover=cover;
		return for_make;
	}
	struct SegmentTree
	{
		int min;//需要维护区间min
		int cover;//需要维护这个位置的块是否被覆盖
		LazyTag tag;
	}sgt[MAXN*4];
	#define LSON (now<<1)
	#define RSON (now<<1|1)
	#define MIDDLE ((left+right)>>1)
	#define LEFT LSON,left,MIDDLE
	#define RIGHT RSON,MIDDLE+1,right
	#define NOW now_left,now_right
	void PushUp(int now)//合并区间min
	{
		sgt[now].min=min(sgt[LSON].min,sgt[RSON].min);
	}
	void Build(int now=1,int left=1,int right=k)//建树
	{
		if(left==right)
		{
			sgt[now].min=min_num;//开始都是区间中的最小数
			sgt[now].cover=0;
			return;
		}
		Build(LEFT);
		Build(RIGHT);
		PushUp(now);
	}
	void Down(LazyTag tag,int now)//修改
	{
		if(!tag.if_cover)
		{
			return;
		}
		sgt[now].tag.if_cover=1;//有覆盖表紧
		sgt[now].tag.cover=tag.cover;
		sgt[now].min=sgt[now].cover=tag.cover;//区间min和覆盖都赋值为cover
	}
	void PushDown(int now)//下传标记
	{
		Down(sgt[now].tag,LSON);
		Down(sgt[now].tag,RSON);
		sgt[now].tag.Clean();
	}
	void Cover(int now_left,int now_right,int cover,int now=1,int left=1,int right=k)//区间覆盖
	{
		if(now_right<left||right<now_left)
		{
			return;
		}
		if(now_left<=left&&right<=now_right)
		{
			Down(MakeTag(cover),now);
			return;
		}
		PushDown(now);
		Cover(NOW,cover,LEFT);
		Cover(NOW,cover,RIGHT);
		PushUp(now);
	}
	void UpdataMin(int place,int min,int now=1,int left=1,int right=k)//单点修改最小值,在左右块被修改后最小值也可能会发生改变
	{
		if(place<left||right<place)
		{
			return;
		}
		if(left==right)
		{
			sgt[now].min=min;
			return;
		}
		PushDown(now);
		UpdataMin(place,min,LEFT);
		UpdataMin(place,min,RIGHT);
		PushUp(now);
	}
	int QueryMin(int now_left,int now_right,int now=1,int left=1,int right=k)//查询区间min
	{
		if(now_right<left||right<now_left)
		{
			return INF;
		}
		if(now_left<=left&&right<=now_right)
		{
			return sgt[now].min;
		}
		PushDown(now);
		int result=min(QueryMin(NOW,LEFT),QueryMin(NOW,RIGHT));
		return result;
	}
	int Query(int place,int now=1,int left=1,int right=k)//单点查询这个块是否被覆盖
	{
		if(place<left||right<place)
		{
			return 0;
		}
		if(left==right)
		{
			int result=sgt[now].cover;
			sgt[now].cover=0;//相当于下传,所以需要清空
			return result;
		}
		PushDown(now);
		return Query(place,LEFT)+Query(place,RIGHT);
	}
	#undef LSON
	#undef RSON
	#undef MIDDLE
	#undef LEFT
	#undef RIGHT
	#undef NOW
}
namespace Range//维护区间的线段树
{
	int first_tree=0;//表示编号小于first_tree的节点为初始线段树上的
	struct LazyTag
	{
		int if_cover;
		int cover;
		void Clean()
		{
			if_cover=0;
		}
	}for_make;
	LazyTag MakeTag(int cover)
	{
		for_make.if_cover=1;
		for_make.cover=cover;
		return for_make;
	}
	struct SegmentTree
	{
		int min;
		int lson,rson;
		LazyTag tag;
	}sgt[MAXN*64];
	int sgt_cnt=0;
	#define LSON sgt[now].lson
	#define RSON sgt[now].rson
	#define MIDDLE ((left+right)>>1)
	#define LEFT LSON,left,MIDDLE
	#define RIGHT RSON,MIDDLE+1,right
	#define NOW now_left,now_right
	void PushUp(int now)
	{
		sgt[now].min=min(sgt[LSON].min,sgt[RSON].min);
	}
	void Build(int &now,int left=1,int right=n)//建树
	{
		now=++sgt_cnt;
		if(left==right)
		{
			sgt[now].min=arr[left];
			return;
		}
		Build(LEFT);
		Build(RIGHT);
		PushUp(now);
		if(left==1&&right==n)
		{
			first_tree=sgt_cnt;
		}
	}
	void Down(LazyTag tag,int &now)
	{
		if(!tag.if_cover)
		{
			return;
		}
		if(now<first_tree)//如果当前节点在初始线段树上那么就新建一个节点,需要将左右子节点也赋值过来
		{
			sgt[++sgt_cnt].lson=sgt[now].lson;
			sgt[sgt_cnt].rson=sgt[now].rson;
			now=sgt_cnt;
		}
		sgt[now].min=tag.cover;
		sgt[now].tag.if_cover=1;
		sgt[now].tag.cover=tag.cover;
	}
	void PushDown(int now)
	{
		Down(sgt[now].tag,LSON);
		Down(sgt[now].tag,RSON);
		sgt[now].tag.Clean();
	}
	void Cover(int now_left,int now_right,int cover,int &now,int left=1,int right=n)
	{
		if(now_right<left||right<now_left)
		{
			return;
		}
		if(now<first_tree)//同理建新节点
		{
			sgt[++sgt_cnt].lson=sgt[now].lson;
			sgt[sgt_cnt].rson=sgt[now].rson;
			now=sgt_cnt;
		}
		if(now_left<=left&&right<=now_right)
		{
			Down(MakeTag(cover),now);
			return;
		}
		PushDown(now);
		Cover(NOW,cover,LEFT);
		Cover(NOW,cover,RIGHT);
		PushUp(now);
	}
	int QueryMin(int now_left,int now_right,int now,int left=1,int right=n)//区间取min
	{
		if(now_right<left||right<now_left)
		{
			return INF;
		}
		if(now_left<=left&&right<=now_right)
		{
			return sgt[now].min;
		}
		PushDown(now);
		return min(QueryMin(NOW,LEFT),QueryMin(NOW,RIGHT));
	}
	#undef LSON
	#undef RSON
	#undef MIDDLE
	#undef LEFT
	#undef RIGHT
	#undef NOW
}
int main()
{
	scanf("%d%d",&n,&k);
	REP(i,1,n)
	{
		scanf("%d",&arr[i]);
		min_num=min(min_num,arr[i]);
	}
	Range::Build(root[0]);
	All::Build();
	REP(i,1,k)
	{
		root[i]=root[0];
	}
	scanf("%d",&m);
	int opt,l,r,x;
	int l_,r_;
	int l_c,r_c;
	int answer=0;
	REP(i,1,m)
	{
		scanf("%d%d%d",&opt,&l,&r);
		l_=l/n+(bool)(l%n);//l和r所在块的编号
		r_=r/n+(bool)(r%n);
		if(opt==1)//修改操作
		{
			scanf("%d",&x);
			if(l_+1<=r_-1)//如果中间存在块,那么中间的块赋值为x
			{
				All::Cover(l_+1,r_-1,x);
			}
			l_c=All::Query(l_);//查询左右的块是否被覆盖,如果覆盖那么就直接修改
			if(l_c)
			{
				Range::Cover(1,n,l_c,root[l_]);
			}
			r_c=All::Query(r_);
			if(r_c)
			{
				Range::Cover(1,n,r_c,root[r_]);
			}
			if(l_^r_)//如果左右所在不同
			{
                //可以画个图理解一下
				Range::Cover(l-(l_-1)*n,n,x,root[l_]);
				Range::Cover(1,r-(r_-1)*n,x,root[r_]);
			}
			else//如果在同一个快
			{
				Range::Cover(l-(l_-1)*n,r-(r_-1)*n,x,root[l_]);
			}
            //在左右的块中修改
			All::UpdataMin(l_,Range::QueryMin(1,n,root[l_]));
			All::UpdataMin(r_,Range::QueryMin(1,n,root[r_]));
		}
		if(opt==2)
		{
			answer=INF;
			if(l_+1<=r_-1)//如果中间有快那么查询中间快的min
			{
				answer=All::QueryMin(l_+1,r_-1);
			}
			l_c=All::Query(l_);
			if(l_c)//和修改部分同理
			{
				Range::Cover(1,n,l_c,root[l_]);
			}
			r_c=All::Query(r_);
			if(r_c)
			{
				Range::Cover(1,n,r_c,root[r_]);
			}
			if(l_^r_)//查询也需要分类讨论
			{
				answer=min(answer,min(Range::QueryMin(l-(l_-1)*n,n,root[l_]),Range::QueryMin(1,r-(r_-1)*n,root[r_])));
			}
			else
			{
				answer=min(answer,Range::QueryMin(l-(l_-1)*n,r-(r_-1)*n,root[l_]));
			}
			printf("%d\n",answer);
		}
	}
	return 0;
}

标签:now,RMQ,min,int,cover,sgt,Periodic,right,CF803G
来源: https://www.cnblogs.com/Sxy_Limit/p/12715214.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有