「CF803G Periodic RMQ Problem」
题目大意
给出一个长度为 \(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\) 建成的树)相同.
当然这里需要动态开点.
然后是修改操作,考虑将修改部分变成三段,分别为左端点所在的块,右端点所在的块,中间的块,然后对于每一段进行修改.
- 对于左右的块,直接在左右块对于的线段树上修改就可以了
- 对于中间的块,显然不可以暴力修改,那么考虑再用一颗线段树维护
然后是查询操作,仍然将它分成三段来查询,和修改的方式差不多.
但是在查询左右块时需要查询左右快是否被覆盖,如果被覆盖那么在维护所有块的线段树中将这个标记去掉,再进行全局覆盖.
时间复杂度 \(\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;
}