• 【CF1335E】 Three Blocks Palindrome 题解


    首先,发现 E1 的数据范围很小,我们可以考虑这样一个算法:

    • 从前往后枚举第一个位置 $i$,从后往前枚举第二个位置 $j$,同时保证 $0 le i<j le n+1$。
    • 枚举取值 $k=1 sim 26$,对于每个 $k$ 分别找到区间 $[1,i], [j,n]$ 中 $k$ 的出现次数,将二者取较小值得到 $ans_1$。
    • 中间的部分,找到区间 $[i+1,j-1]$ 中出现次数最多的数,并将其出现次数即为 $ans_2$,最终答案即为 $2 imes ans_1+ans_2$。

    当然直接暴力是过不去的,我们可以将数字 $1 sim 26$ 在序列每一个位置的出现次数都记录一个前缀和,这样在查询某个字符在某个区间的出现次数时可以直接 $O(1)$ 计算得到。

    该算法的时间复杂度为 $O(n^2 imes a_i)$,3s 内肯定能过。


    接下来考虑如何优化这个算法。

    优化一

    我们发现,在枚举 $i$ 的过程中,当 $j$ 一定时,相比于 $i-1$ 只有 $a_i$ 的前缀出现次数发生了改变。

    所以不必枚举取值,每次枚举 $i,j$ 时直接令 $k=a_i$ 即可,时间复杂度降为 $O(n^2)$。

    优化二

    根据优化一,对于枚举的的每一个 $i$,都最多只有一个位置 $j$ 同时满足以下两个条件:

    • $a_i=a_j$
    • $sum_{x=1}^{i-1}[a_x=a_i]=sum_{x=j+1}^n[a_x=a_i]$

    因此可以针对不同的取值 $k$,可以记录序列中第 $1,2,...$ 个数值等于 $k$ 的位置。

    在每次枚举 $i$ 的时候,合适的 $j$ 就可以直接通过记录被直接找到。

    然后中间的部分只需要枚举取值 $k$,通过前缀和找到 $[i+1,j-1]$ 中出现次数最多的 $k$,该算法的时间复杂度就降为了 $O(n imes max{a_i})$。

    此时已经可以通过本题。


    节省空间的解法

    前文提到的前缀和数组,理论上开 $4e7$ 的 int 是可以满足空间限制的。

    但是我在做题的时候把空间算错了,就以为上述算法无法通过,于是又想了一个节省空间,但效率稍低的算法。

    既然没法记前缀和了,我们可以用一些其它的方式求区间和。

    我们开 200 个 vector(下文定义变量名为 `v`),第 $i$ 个 vector 按从前到后的顺序记录原序列中数值等于 $i$ 的位置

    • 按 $n ightarrow1$ 逐个枚举 $i$,这样做是为了方便之后的查找数列单调不降,方便之后使用 STL 的二分(当然手写二分也行)。
    • 记录 $a_i=k$,二分找 $k$ 是整个序列中第几个等于 $k$ 的数,那么就知道 $[i,n]$ 有不同的 $k$ 的数量,在此记为 $num$。
    • 同理,可以找到一个满足 $sum_{x=1}^{j}[a_x=a_i]=sum_{x=j}^n[a_x=a_i]$ 的 $j$,这可以查找 `v[a[i]][num - 1]` 得到。
    • 接下来查找中间的部分 $[j+1,i-1]$。
    • 枚举在 $1sim200$ 内的取值 $k$。
    • 找到所有等于 $k$ 的数中第一个大于 $i-1$ 的数在 vector 中的下标 $pos1$。
    • 找到所有等于 $k$ 的数中第一个大于等于 $j+1$ 的数在 vector 中的下标 $pos2$。
    • $pos1-pos2$ 就是区间 $[j+1,i-1]$ 中等于 $k$ 的数字数量。
    • 对于相同的 $i,j$ 找到最大的一个 $pos1-pos2$ 加上 $2num$ 即为答案。

    由于两百个 vector 一共就存 $n$ 个数,该算法空间复杂度降为 $O(n)$。

    当然,该算法对时间作出了一定牺牲。瓶颈在于查找 $pos1,pos2$ 相比原算法多了一个二分,但两百次 vector 二分的长度之和等于 $n$,所以算法的时间复杂度为 $O(n imes max{a_i} imes k)$,$k$ 最大约等于 $10$(不含 vector 自身常数)。

    该做法不经过任何常数优化也可以通过,详情见代码。

    代码(最慢点 1.67s,空间最大 3.35MB):

    int t, n, a[200010], q[210], h[210], ans;
    vector <int> v[210];
     
    inline int max(int a, int b){
        return a > b ? a : b;
    }
     
    int main(){
     
        scanf("%d", &t);
        while(t--){
            scanf("%d", &n);
            for(int i = 1; i <= n; i++)
                scanf("%d", &a[i]);
            for(int i = 1; i <= 200; i++)
                q[i] = h[i] = 0, v[i].clear();
            ans = 0;
            for(int i = 1; i <= n; i++)
                v[a[i]].push_back(i), q[a[i]]++, ans = max(ans, q[a[i]]);
            for(int i = n, pos, now, poss; i >= 1; i--){
                poss = lower_bound(v[a[i]].begin(), v[a[i]].end(), i) - v[a[i]].begin();
                poss = v[a[i]].size() - poss;
                pos = v[a[i]][poss - 1];
                if(pos >= i) continue;
                now = 0;
                for(int j = 1, pos1, pos2; j <= 200; j++){
                    pos1 = lower_bound(v[j].begin(), v[j].end(), i) - v[j].begin();
                    pos2 = upper_bound(v[j].begin(), v[j].end(), pos) - v[j].begin();
                    now = max(now, pos1 - pos2);
                }
                ans = max(ans, poss * 2 + now);
            }
            printf("%d
    ", ans);
        }
     
        return 0;
    }
  • 相关阅读:
    C#编程利器之二:结构与枚举(Structure and enumeration)
    解读设计模式模板方法模式(Template Method),电脑就是这样造出来的
    清空mysql一个库中的所有表
    在执行并行程序工程中,突然弹出 connection closed 窗口,随后 ssh 与服务器的连接断开,并行程序也中断
    菜鸟求救 myeclipse安装flex3插件的问题
    linux 下 将 shell script 与 一个桌面图标联系在一起 (2)
    MYSQL EXPLAIN语句的extended 选项学习体会
    MySQL 性能跟踪语句
    Flex Flash
    Flex Builder 3 正式版
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/14453971.html
Copyright © 2020-2023  润新知