清北学堂模拟赛d1t3 听音乐(music)

题目描述

LYK喜欢听音乐,总共有n首音乐,有m个时刻,每个时刻LYK会听其中一首音乐,第i个时刻会听第ai首音乐。它给自己定了一个规定,就是从听音乐开始,听的每连续n首音乐都是互不相同的。例如当n=3时,从听歌开始,123321就是一个合法的顺序(此时LYK听了两轮歌,分别是123和321,每一轮的歌都是互不相同的),而121323就是一个不合法的顺序(LYK也听了两轮歌,第一轮中121存在听了两次相同的歌)。我们现在只截取其中一个片段,也就是说并不知道LYK之前已经听了什么歌。因此121323也仍然可以是一个合法的顺序,因为LYK之前可能听过3,然后再听121323,此时LYK听了三轮歌,分别是312,132和3。

现在LYK将告诉你这m个时刻它听的是哪首歌。你需要求出LYK在听这m首歌之前可能听过的歌的不同方案总数(我们认为方案不同当且仅当之前听过的歌的数量不同)。LYK向你保证它之前听过的歌的数量是在0~n-1之间的。因此你输出的答案也应当是0~n中的某个整数(答案是0表示LYK记错了,没有一个合法的方案)。

输入格式(music.in)

    第一行两个数n,m。

    第二行m个数表示ai。

输出格式(music.out)

    一个数表示答案。

输入样例1

4 10

3 4 4 1 3 2 1 2 3 4

输出样例1

1

样例解释1:LYK之前一定只听过2首歌(12或者21),这样可以分成3部分分别是34,4132,1234,每一部分都没有出现相同的歌。对于其它情况均不满足条件。

输入样例2

6 6

6 5 4 3 2 1

输出样例2

6

样例解释2:LYK之前听过0~5首歌的任意几首都是有可能满足条件的。

数据范围

对于50%的数据n,m<=1000。

对于100%的数据1<=n,m<=100000,1<=ai<=n。

其中均匀分布着n<m以及n>=m的情况。

提示:

LYK知道这个题目很长,但为了便于理解已经加了很多注释了……建议没看懂的同学们再重新看一遍……

分析:比较有意思的一道题,首先我们最多将序列分成m/n + 2段,那么我们花O(n)的时间枚举最前面一段,再花O(m/n + 2)的时间枚举中间段,如果能够做到O(1)查询,那么复杂度就是O(n + m).

      考虑怎么O(1)查询,当然边枚举边处理肯定是不行的,我们需要预处理.每次维护一个长度为n的区间[l,r],每次移动区间:r++,l++,同时v2[a[r]]++,v2[a[l]]--,最后有没有v2等于2的,事实上就是一个动态维护.记录下当前左端点是否可行即可.对于最前面和最后面的区间我们分别记录一个前缀和一个后缀就可以了.最后枚举要分多少段,在维护后缀的时候同时扫一下看看能不能满足要求即可.

如果我们把算法看做是一个又一个操作的和,那么我们看看能优化哪些操作的复杂度,比如本题的操作就有枚举操作和查询操作,枚举是O(n)不能优化了,尽最大的可能去优化查询操作.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n, m, a[100010], v[100010], V[100010],sum,V2[100010],ans;

bool check(int x)
{
    int i;
    for (i = x - n + 1; i >= 1; i -= n)
        if (!V2[i])
            return false;
    i += n;
    if (i - 1 > 0 && !V[i - 1])
        return false;
    return true;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= min(n, m); i++)
    {
        v[a[i]]++;
        if (v[a[i]] == 2)
            sum++;
        if (!sum)
            V[i] = 1;
    }
    if (!sum)
        V2[1] = 1;
    for (int i = n + 1; i <= m; i++)
    {
        v[a[i]]++;
        if (v[a[i]] == 2)
            sum++;
        v[a[i - n]]--;
        if (v[a[i - n]] == 1)
            sum--;
        if (!sum)
            V2[i - n + 1] = 1;
    }
    memset(v, 0, sizeof(v));
    sum = 0;
    int i = 0;
    for (i = m; i >= 1; i--)
    {
        v[a[i]]++;
        if (v[a[i]] >= 2)
            break;
    }
    for (int j = m; j > max(0, m - n); j--)
        if (i <= j && check(j))
            ans++;
    if (ans)
        printf("%d
", (n >= m && ans == m ? n : ans));
    else
        printf("0
");
return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7617583.html