求静态RMQ问题

  • 线段树求静态RMQ问题

在这里插入图片描述

  • 首先,对于每个数 i i i 显然要维护,以 i i i 结尾的最长不重复子段的长度

    维护 s t a r t i start_i starti 表示以 i i i 结尾,最长不重复子段的开始位置
    那么, i − s t a r t i + 1 i-start_i+1 istarti+1 就是以 i i i 结尾的最长不重复子段的长度

    维护 s t a r t i start_i starti
    1 ∣ 1| 1. s t a r t i start_i starti 肯定在上一个 a i a_i ai 出现的位置 + 1 +1 +1 的位置及以后
    2 ∣ 2| 2. s t a r t i start_i starti肯定在 f i − 1 f_{i-1} fi1 的位置及以后 (如果上一个都有重复那么下一个一定有重复)

    所以 s t a r t [ i ] = m a x ( f [ i − 1 ] , l s t [ a [ i ] ] + 1 ) start[i]=max(f[i-1],lst[a[i]]+1) start[i]=max(f[i1],lst[a[i]]+1) 即为满足要求

	cin>>n>>q;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		start[i]=max(start[i-1],lst[a[i]]+1);
		lst[a[i]]=i;
	}
  • 而对于每个问题 [ L , R ] [L,R] [L,R]
    s t a r t i start_i starti 已知的情况下,令 f ( i ) f(i) f(i) 表示在区间内以 i i i 结尾的最长合法子段长度,分以下几种情况:

    对于 L ≤ i ≤ R L\leq i \leq R LiR :

    1 ∣ 1| 1 s t a r t i start_i starti L L L 左侧
    那么 f ( i ) = i − L + 1 f(i)=i-L+1 f(i)=iL+1

    2 ∣ 2| 2 s t a r t i start_i starti L L L L L L 右侧
    那么 f ( i ) = i − s t a r t i + 1 f(i)=i-start_i+1 f(i)=istarti+1

    最终对于询问的结果应该为 m a x ( f L . . . . f R ) max(f_L....f_R) max(fL....fR)

	//60pts
	while(q--)
	{
		int l,r,res=0;
		cin>>l>>r;
		for(int i=l;i<=r;i++)
		{
			if(start[i]<l) res=max(res,i-l+1);
			else res=max(res,i-start[i]+1);
		}
		cout<<res<<endl;
	}

问题相当于 静态维护区间 f i f_i fi 最大值

考虑到 f i f_i fi 的不减性,我们可以用 l o w e r lower lower_ b o u n d bound bound 二分查找出第一个 s t a r t i ≤ L start_i \leq L startiL p o s pos pos ,那么 p o s − 1 pos-1 pos1 就是位置最大的 s t a r t i < L start_i < L starti<L 的数

如上文所述,对于 L ≤ i ≤ R L\leq i \leq R LiR ,若 s t a r t i start_i starti L L L 左侧,那么 f ( i ) = i − L + 1 f(i)=i-L+1 f(i)=iL+1

那么这个数在所有的 s t a r t i < L start_i < L starti<L 的数中的 f ( i ) f(i) f(i) 也一定是最大的为 p o s − L + 1 pos-L+1 posL+1

而对于 i > = p o s i>=pos i>=pos 的情况,即 s t a r t i > = L start_i >= L starti>=L f ( i ) = i − s t a r t i + 1 f(i)=i-start_i+1 f(i)=istarti+1 ,用 S T ST ST 表 或 线段树维护

#include<bits/stdc++.h>
#define lson pos<<1
#define rson pos<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int MAXN=1e6+5;
int n,q,a[MAXN],start[MAXN],lst[MAXN],f[MAXN],st[MAXN][20];
void build()
{
	for(int i=1;i<=n;i++) st[i][0]=f[i];
	for(int j=1;j<=log2(n);j++)
		for(int i=1;i+(1<<j)-1<=n;i++)
			st[i][j]=max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
}
int query(int l,int r)
{
	int t=log2(r-l+1);
	return max(st[l][t],st[r-(1<<t)+1][t]);
}
int main()
{
	freopen("norep.in","r",stdin);
	freopen("norep.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		start[i]=max(start[i-1],lst[a[i]]+1);
		lst[a[i]]=i;
	}
	for(int i=1;i<=n;i++)	
		f[i]=i-start[i]+1;
		
	build();
	while(q--)
	{
		int l,r,res=0;
		cin>>l>>r;
		int pos=lower_bound(start+l,start+r,l)-start;
		if(start[r]<l) res=max(res,r-l+1);
		else
		{
			if(pos-1>=l) res=max(res,pos-1-l+1);
			if(pos<=r) res=max(res,query(pos,r));
		}
		cout<<res<<endl;
	}
	return 0;
}
#include<bits/stdc++.h>
#define lson pos<<1
#define rson pos<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int MAXN=1e6+5;
int n,q,a[MAXN],start[MAXN],lst[MAXN],f[MAXN],tree[MAXN<<4];
void pushup(int pos)
{
	tree[pos]=max(tree[lson],tree[rson]);
}
void build(int pos,int l,int r)
{
	if(l==r) {tree[pos]=f[l]; return ;}
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(pos);
}
int query(int pos,int l,int r,int x,int y)
{
	if(x<=l && r<=y) return tree[pos];
	int res=0;
	if(x<=mid) res=max(res,query(lson,l,mid,x,y));
	if(y>mid)  res=max(res,query(rson,mid+1,r,x,y));
	return res;
}
int main()
{
	freopen("norep.in","r",stdin);
	freopen("norep.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		start[i]=max(start[i-1],lst[a[i]]+1);
		lst[a[i]]=i;
	}
	for(int i=1;i<=n;i++)	
		f[i]=i-start[i]+1;
		
	build(1,1,n);
	while(q--)
	{
		int l,r,res=0;
		cin>>l>>r;
		int pos=lower_bound(start+l,start+r,l)-start;
		if(start[r]<l) res=max(res,r-l+1);
		else
		{
			if(pos-1>=l) res=max(res,pos-1-l+1);
			if(pos<=r) res=max(res,query(1,1,n,pos,r));
		}
		cout<<res<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值