NC50446 [与众不同] 题解

与众不同

https://ac.nowcoder.com/acm/problem/50446

题意简化:

给定一个长度为的序列,m个询问,每次询问一个区间内最长的没有重复数字的子序列,以及 <=
样例:

9 2
2 5 4 1 2 3 6 2 4
0 8
2 6

output:

6
5

思路一:

简单暴力,枚举区间内的子序列,然后O扫一遍,总的时间复杂度是O

思路二:

借助线段树,枚举子区间,判断,总的时间复杂度是:O

显然上面两个都是错误的,但是第二个貌似时间上更优秀......
继续想!

思路三:

二分最长的长度,对于判断,每一次枚举左端点,那么右端点便是固定的,然后借助线段树O

上面这个解法已经比思路一和二优秀很多了。但是还是过不掉这道题,怎么办?

继续思考。

思路四:

记录每一个元素的前一个与它相同的元素的位置是哪一个。

我们发现,对于一个左端点来说,其最长的,没有重复数字的子序列的长度是固定的。

固定左端点后,我们总是希望不停的往右边挪动右端点以求能够有一个最长的子序列。

实际上,我们对于每一个左端点,不停移动右端点(这个右端点只会增加而不会减小,你不妨手玩几组,想一想,为什么),总的时间复杂度O(),可行,动手写代码。

对于每一个点,当它为左端点时不妨设其最长没有重复元素的子段的右端点为:

同时,我们观察到,得到的序列是单调不降的,于是就方便处理,用二分.

对于一个查询怎么办?
区间 之间的任意一个点作为左端点

  • Case 1 左端点对应的最长的没有重复元素的子序列的右端点大于

    显然这些左端点只有最左边的那个是"有用"的,同时因为得到的是单调不降的,我们很容易用二分找到这一个最左边的大于的点。

  • Case2 左端点对应的最长的没有重复元素的子序列的右端点小于等于的,

    取最大的(预处理出),ST表 线段树维护即可,但是这里显然用ST表更优。

比较上下两种情况即可

总的时间复杂度为:O

Code :

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005 , MAXM = 200005;
int n , m,M ,flag,tot = 1;
int a[MAXN],Last[MAXN],book[MAXN],R[MAXN];
int T[MAXN],Max[MAXN][25];

struct Node {
    int data,id;
} P[MAXN];

void Prepare();
int cmp(Node A, Node B);
int GetMax(int l,int r)
{
    if(l > r)return 0;
    int k = log2(r - l + 1);
    return max(Max[l][k] , Max[r - (1 << k) + 1][k]);
}

int main()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i ++)
        cin >> a[i],P[i].id = i , P[i].data = a[i];
    Prepare();//离散化等预处理
    for(int i = 1 ; i <= n ; i ++)
    {
        int S = R[i - 1];
        while(S + 1 <= n)
        {
            if(i <= Last[S + 1] && Last[S + 1] <= S)break;
            S ++;//只增不降
        }
        R[i] = S;
        T[i] = R[i] - i + 1;
        Max[i][0] = T[i];
    }
    for(int j = 1 ; j <= log2(n) ; j ++)
        for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
    Max[i][j] = max(Max[i][j - 1] , Max[i + (1 << (j - 1))][j - 1]);
    for(int v = 1 ; v <= m ; v ++)
    {
        int l,r;
        cin >> l >> r;
        l ++ , r ++;
        int S = r,Ans = -1;
        for(int i = log2(r - l + 1) + 1; i >= 0; i --)
        if(S - (1 << i) >= l && R[S - (1 << i)] > r)S = S - (1 << i);//二分我换成了倍增
        Ans = r - S + 1;//Case 1
        Ans = max(Ans,GetMax(l,S - 1));
        cout << Ans << endl;
    }
    return 0;
}

void Prepare()
{
    sort(P + 1 , P + 1 + n , cmp);
    for(int i = 1 , now = 1; i <= n ; i ++)
    {
        if(P[i].data != P[i - 1].data)now ++;
        a[P[i].id] = now;
    }//离散化
    R[0] = 1;
    for(int i = 1 ; i <= n ; i ++)
    {
        Last[i] = book[a[i]];
        book[a[i]] = i;
        M = max(Last[i],M);
    }//找last[i]
    return ;
}

int cmp(Node A , Node B)
{
    return A.data < B.data;
}
闲人碎语 文章被收录于专栏

刊载算法学习笔记以及一些题解

全部评论

相关推荐

dongsheng66:如果想进大厂的话,在校经历没必要占这么大篇幅,可以把专业技能单独放一个专栏写,可以加个项目经历
点赞 评论 收藏
分享
工作基本定了,比较满意。这深度学习真是难搞啊,论文看不懂,代码看不懂,不会改,创新点???&nbsp;还有一个月中期,怎么搞,已经抑郁了,没有小论文要求,我真是非常幸运了,不然就我这科研能力…………&nbsp;感觉自己对自己失去信心,无时无刻不在否定自己取得一些成绩都是运气好罢了,真的能顺利毕业吗?严重内耗、无力感爆棚啊…………
AI牛可乐:哎呀,听起来你最近压力好大呀,毕业论文和工作的双重压力一定让你感觉像是背了两座山呢。深度学习确实挺复杂的,就像是要在一片迷雾中找到出路一样。但是,你已经很棒了,工作都定好了,这是很大的成就呢! 论文的事情,别太担心。每个人都有看不懂代码和论文的时候,这是正常的。至于创新点嘛,有时候灵感就像小猫一样,需要一点一点地哄出来的。你可以先试试把你看过的资料整理一下,说不定就能找到新的思路呢。 中期答辩前,你可以列个计划,一步一步来,把大问题分解成小问题,一点一点克服。记得,你不是一个人在战斗,你的导师、同学都是你的战友哦。 至于信心嘛,别否定自己,每个人都会有低谷的时候。你已经取得的成绩,都是你实力的体现。毕业不是问题,只要你不放弃,就一定能顺利走过去的。加油哦,我相信你可以的! 对了,如果你愿意的话,可以点击我的头像,我们私信聊,也许我能帮你出出主意,或者就是简单地聊聊天,分散一下你的注意力也好呀。🐮💪🌟
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务