【模板】回滚莫队&不删除莫队

题目背景

这是一道模板题。

题目描述

给定一个序列,多次询问一段区间 [ l , r ] [l,r] [l,r],求区间中相同的数的最远间隔距离

序列中两个元素的间隔距离指的是两个元素下标差的绝对值

输入格式

第一行一个整数 n n n,表示序列长度。

第二行 n n n 个整数,描述这个序列。

第三行一个整数 m m m,表示询问个数。

之后 m m m 行,每行两个整数 l , r l,r l,r 表示询问区间。

输出格式

m m m 行,每行一个整数表示答案。如果区间内不存在两个数相同,则输出 0 0 0

样例 #1

样例输入 #1

8
1 6 2 2 3 3 1 6
5
1 4
2 5
2 8
5 6
1 7

样例输出 #1

1
1
6
1
6

提示

a i a_i ai 表示序列元素。

对于 40 % 40\% 40% 的数据,满足 1 ≤ a i ≤ 400 1\leq a_i \leq 400 1ai400 1 ≤ n , m ≤ 60000 1\leq n,m\leq 60000 1n,m60000

对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 2 ⋅ 1 0 5 1\leq n,m\leq 2\cdot 10^5 1n,m2105 1 ≤ a i ≤ 2 ⋅ 1 0 9 1\leq a_i\leq 2\cdot 10^9 1ai2109

题目思路

板子题
将询问编号按升序排序
那么具体的情况我们可以分成两种:

1、如果这个询问的左右端点在同一个块,那么我们可以直接暴力扫。
2、不在同一个块的,根据之前的排序,保证了左端点为同一个块时右端点的单调递增,这时我们只需要让左端点可以一直往左扫即可。

Code


#include<bits/stdc++.h>
using namespace std;
int n,m,a[200000+10],b[200000+10],pos[200000+10],L[200000+10],R[200000+10],mp[200000+10],ma[200000+10],mi[200000+10],fans[200000+10],block=0,ans=0;
struct node
{
    int l,r,id;
}q[200000+10];
int read()
{
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        s=s*10+(ch-'0'),ch=getchar();
    return s*w;
}
bool cmp(node a,node b)
{
    if(pos[a.l]!=pos[b.l])
        return pos[a.l]<pos[b.l];
    else
        return a.r<b.r;
}
int main()
{
    n=read();
    block=sqrt(n);
    for(int i=1;i<=n;++i)
    {
        b[i]=a[i]=read();
        pos[i]=(i-1)/block+1;
    }
    for(int i=1;i<=pos[n];++i)
        L[i]=(i-1)/block+1,R[i]=i*block;
    if(R[pos[n]]>n)
        R[pos[n]]=n;
    sort(b+1,b+n+1);
    int siz=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i)
        a[i]=lower_bound(b+1,b+siz+1,a[i])-b;
    m=read();
    for(int i=1;i<=m;++i)
    {
        int l=read(),r=read();
        q[i]=(node){l,r,i};
    } 
    sort(q+1,q+m+1,cmp);
    int l=1,r=0,lastblock=0;
    for(int i=1;i<=m;++i)
    {
        if(pos[q[i].l]==pos[q[i].r])
        {
            int maxn=0;
            for(int j=q[i].r;j>=q[i].l;--j)
            {
                if(!mp[a[j]])
                    mp[a[j]]=j;
                else
                    maxn=max(maxn,mp[a[j]]-j);
            }
            for(int j=q[i].r;j>=q[i].l;--j)
                mp[a[j]]=0;
            fans[q[i].id]=maxn;
            continue;
        }
        if(pos[q[i].l]!=lastblock)
        {
            while(r>R[pos[q[i].l]])
            {
                ma[a[r]]=mi[a[r]]=0;
                r--;
            }
            while(l<R[pos[q[i].l]]+1)
            {
                ma[a[l]]=mi[a[l]]=0;
                l++;
            }
            r=l-1;
            ans=0;
            lastblock=pos[q[i].l];
        }
        while(r<q[i].r)
        {
            r++;
            if(mi[a[r]]==0)
                ma[a[r]]=mi[a[r]]=r;
            else
                ma[a[r]]=r,ans=max(ans,ma[a[r]]-mi[a[r]]);
        }
        int l2=l,maxn=ans;
        while(l2>q[i].l)
        {
            l2--;
            if(ma[a[l2]]==0)
                ma[a[l2]]=l2;
            else
                maxn=max(maxn,ma[a[l2]]-l2);
        }
        while(l2<l)
        {
            if(ma[a[l2]]==l2)
                ma[a[l2]]=0;
            l2++;
        }
        fans[q[i].id]=maxn;
    }
    for(int i=1;i<=m;++i)
        printf("%d\n",fans[i]);
    return 0;
}   

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值