zoukankan      html  css  js  c++  java
  • 「CF803G Periodic RMQ Problem」

    题目大意

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

    分析

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

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

    可以发现如果暴力建树的复杂度是 (mathcal{O}(nklog_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
    ",answer);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    爬虫基础1 怎么使用浏览器查看网络请求
    前端模块化总结(commonJs,AMD,CMD,ES6 Module)
    js强制类型转换规则
    vue-cli3中使用mxgraph的一些总结
    js概念笔记(this)
    js概念笔记(for循环,模块化)
    根据对象数组的某一属性排序
    从svg诞生的panda
    gulp笔记(从开发到发布)
    webpack4.x学习笔记
  • 原文地址:https://www.cnblogs.com/Sxy_Limit/p/12715214.html
Copyright © 2011-2022 走看看