「CF522D Closest Equals」
题目大意
给出一个序列,每次查询一段区间内距离最近的相同元素之间的距离.
分析
RMQ+二分的做法好妙啊,我就根本想不出来.
考虑一个数对只有当两个数相等且都在区间内才可能会产生贡献.
记录一个 \(pre_i\) 表示第 \(i\) 个数相同的数上一次出现的位置
这个东西就很像区间取 \(\min\) 了,将 \(i\) 与 \(pre_i\) 的贡献放在 \(pre_i\) 的位置,那么这个区间查询的答案就是 \(l\sim r\) 中记录的贡献的最小值了.
可是直接这样搞可能会导致 \(i\) 在区间外 \(pre_i\) 在区间内的情况出现,那么就考虑将查询离线下来,按右端点排序,只有当 \(i\leq right\) 时才将贡献放在 \(pre_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=5e5+5;
const int INF=1e9;
int n,m;
int arr[MAXN];
struct Sor
{
int num,id;
}sor[MAXN];
bool Cmp(Sor a,Sor b)
{
return a.num>b.num;
}
struct SegmentTree//一颗维护区间 min 的线段树
{
int min;
}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)
{
sgt[now].min=min(sgt[LSON].min,sgt[RSON].min);
}
void Build(int now=1,int left=1,int right=n)
{
if(left==right)
{
sgt[now].min=INF;
return;
}
Build(LEFT);
Build(RIGHT);
PushUp(now);
}
void Updata(int place,int num,int now=1,int left=1,int right=n)//单点修改
{
if(right<place||place<left)
{
return;
}
if(left==right)
{
sgt[now].min=num;
return;
}
Updata(place,num,LEFT);
Updata(place,num,RIGHT);
PushUp(now);
}
int Query(int now_left,int now_right,int now=1,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;
}
return min(Query(NOW,LEFT),Query(NOW,RIGHT));
}
#undef LSON
#undef RSON
#undef MIDDLE
#undef LEFT
#undef RIGHT
#undef NOW
int pre[MAXN];
int last[MAXN];
int answer[MAXN];
struct Range
{
int left,right,id;
}range[MAXN];
bool Cmp2(Range a,Range b)
{
return a.right<b.right;
}
int main()
{
scanf("%d%d",&n,&m);
REP(i,1,n)//离散化
{
scanf("%d",&sor[i].num);
sor[i].id=i;
}
sort(sor+1,sor+1+n,Cmp);
sor[0].num=sor[1].num-1;
int tot=0;
REP(i,1,n)
{
if(sor[i].num^sor[i-1].num)
{
++tot;
}
arr[sor[i].id]=tot;
}
REP(i,1,n)//处理 pre 数组
{
pre[i]=last[arr[i]];
last[arr[i]]=i;
}
REP(i,1,m)
{
scanf("%d%d",&range[i].left,&range[i].right);
range[i].id=i;
}
sort(range+1,range+1+m,Cmp2);//将查询的区间按右端点排序
int now=1;
Build();
REP(i,1,m)
{
while(now<=range[i].right)//将小于右端点的贡献放上
{
if(pre[now])
{
Updata(pre[now],now-pre[now]);
}
++now;
}
answer[range[i].id]=Query(range[i].left,range[i].right);
if(answer[range[i].id]==INF)
{
answer[range[i].id]=-1;
}
}
REP(i,1,m)//输出
{
printf("%d\n",answer[i]);
}
return 0;
}
这个东西怎么搞在线 \(\mathcal{O}(m\log n)\) 啊
建可持久化线段树就好了.