[CF522D]Closest Equals——线段树+离线技巧+思维

题目传送门

思路

  • 应该能想到我们需要求出每一个 a i a_i ai 上一次出现的位置 p r e [ i ] pre[i] pre[i] , 对于每一个询问区间 [ l , r ] [l,r] [l,r] 我们需要求出 i − p r e [ i ] i-pre[i] ipre[i] 的最小值 ( p r e [ i ] ≥ l ) (pre[i] \geq l) (pre[i]l)

  • 线段树维护最小值即可

  • 问题1:我们可以将每个 p r e [ i ] pre[i] pre[i] 位置的值换成 i − p r e [ i ] i-pre[i] ipre[i] ,但是每次在线暴力置换一定会超时

  • 我们将询问离线下来,遍历 1 1 1 ~ n n n , 对于每一个 i i i,我们执行修改操作,修改 p r e [ i ] pre[i] pre[i] 位置的值为 i − p r e [ i ] i-pre[i] ipre[i], 这样便不会超时。

  • 问题2:如果只是按照上述操作后再来查询,可能会出现查询时的 i > r i >r i>r,就是 p r e [ i ] pre[i] pre[i] 再区间 [ l , r ] [l,r] [l,r] 内,但是 i i i不一定在。

  • 我们应该这样做,在遍历 1~n 的时候,将询问按照右端点从小到大排序,当 i = = r i==r i==r 的时候,我们就去查询 [ l , r ] [l,r] [l,r], 这样便能正确解决此问题。

AC代码

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x; i<=y; ++i)
#define per(i,x,y) for(int i=x; i>=y; --i)
#define pushk push_back
#define popk pop_back
#define mem(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define ll long long
#define lp p<<1
#define rp p<<1|1
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5e5+9;
int a[N],ans[N];
struct node{
	int l,r,mi;
}tr[N<<2];
struct ASK{
	int l,r,id;
	bool operator<(const ASK &t) const{
		return r<t.r;
	}
}q[N];
unordered_map<int,int> mp;
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r) {
	tr[p].mi = INF;
		return ;
	}
	int mid = (l+r)>>1;
	build(lp,l,mid);
	build(rp,mid+1,r);
	tr[p].mi = min (tr[lp].mi, tr[rp].mi); 
}
void modify(int p,int x,int c){
	if(tr[p].l==tr[p].r) {
		tr[p].mi=c;
		return ;
	}
	int mid = (tr[p].l + tr[p].r)>>1;
	if(x<=mid) modify(lp,x,c);
	else modify(rp,x,c);
	tr[p].mi = min (tr[lp].mi, tr[rp].mi); 
}
int ask(int p,int l,int r){
	if(l<=tr[p].l && tr[p].r<=r) return tr[p].mi;
	int mid = (tr[p].l + tr[p].r)>>1;
	int res=INF;
	if(l<=mid) res=min(res,ask(lp,l,r));
	if(r>mid) res=min(res,ask(rp,l,r));
	tr[p].mi = min (tr[lp].mi, tr[rp].mi); 
	return res;
}
int main() {	

	int n,m;
	cin>>n>>m;
	rep(i,1,n)  scanf("%d",a+i);
	rep(i,1,m){
		scanf("%d %d",&q[i].l,&q[i].r); 
		q[i].id=i;
		ans[i]=INF;
	}
	build(1,1,n);
	sort(q+1,q+1+m);
	int idx=1;
	rep(i,1,n){
		if(mp[a[i]]) modify(1,mp[a[i]],i-mp[a[i]]);
		while(idx<=m && q[idx].r==i){			
			ans[q[idx].id] = ask(1,q[idx].l,q[idx].r);
			idx++; 
		}
		mp[a[i]]=i;
	}
	rep(i,1,m) printf("%d\n",ans[i]==INF?-1:ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值