与众不同

鬼知道我为什么二分的时候传了个常量进去当左右端点还能过样例啊,不是样例就应该直接TLE吗?,调了好久

题意是这样的:给定一个长为n的序列a,再给定m个区间 [(l_i,r_i)] 。对于每个区间输出该区间内最长的子序列并且这个子序列没有重复的元素。

对于全部数据,(1le n,m≤2cdot 10^5,0le l_ile r_ile N−1,|ai|le 10^6)

思路:
像这种没有重复数字的询问有很多都是记录这个数字上次出现的位置,然后再加一些其他的操作的。这题也是,用last[i]表示i最近一次出现的位置,st[i]表示以i为右端点的最长满足条件的起点,则 st[i]=max(st[i-1],last[a[i]]+1) ,设f[i]=i-st[i]+1(就是长度啦).

将[(l_i,r_i)]内的点分成2类:st[i](ge l_i) 的和不属于第1类的。由于st[i]单调不降,所以一定可以找到一个位置tmp,使得[(l_i,tmp-1)]是第二类点,[(tmp,r_i)] 是第一类点。

对于第一类位置,直接找 (max){(f_i)} ((l_ile i le r_i)) 就是答案。用ST表维护一下即可.

对于第二类位置,(tmp-l_i)即为答案。

#include<cstdio>
using namespace std;
const int M=1000005;
const int N=200005;
int last[M<<1],st[N],f[N],mx[N][19],log[N];
inline int max(const int &x,const int &y){return x>y?x:y;}
int find(int l,int r)
{
    if(st[l]==l)return l;
    if(st[r]<l)return r+1;
    int res=l,ll=l,rr=r;
    while(ll<=rr)
    {
        int mid=(ll+rr)>>1;
        if(st[mid]<l)ll=mid+1;
        else res=mid,rr=mid-1;
    }
    return res;
}
int query(int l,int r)
{
    int lo=log[r-l+1];
    return max(mx[l][lo],mx[r-(1<<lo)+1][lo]);
}
int n,m;
int main()
{
    scanf("%d%d",&n,&m);log[0]=-1;
    for(int i=1,x;i<=n;++i)
    {
        scanf("%d",&x);
        st[i]=max(st[i-1],last[x+M]+1);
        last[x+M]=i;
        f[i]=i-st[i]+1;
        log[i]=log[i>>1]+1;
        mx[i][0]=f[i];
    }
    for(int j=1;j<=18;++j)
        for(int i=1;i+(1<<j)-1<=n;++i)
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<j-1)][j-1]);
    for(int i=1,ql,qr;i<=m;++i)
    {
        scanf("%d%d",&ql,&qr);
        ++ql,++qr;
        int tmp=find(ql,qr),ans;
        ans=tmp-ql;
        if(tmp<=qr)ans=max(ans,query(tmp,qr));
        printf("%d
",ans);
    }
    return 0;
}
路漫漫其修远兮,吾将上下而求索
原文地址:https://www.cnblogs.com/zzctommy/p/12342078.html