zoukankan      html  css  js  c++  java
  • 单周赛 248 题解

    本次周赛前三题不难,最后一题比较有难度

    出题人不讲武德,出后缀数据结构来为难大家

    涉及知识点:模拟,贪心,排序,快速幂,向上取整,后缀数组

    基于排列构建数组

    给定一个数组 (nums),构建一个数组 (ans),使得 (ans[i] = numsleft[numsleft[i ight] ight])

    题解

    按照题意写就行

    class Solution {
    public:
        vector<int> buildArray(vector<int>& nums) {
            vector<int> ans(nums.size());
            for (int i = 0; i < nums.size(); ++i) {
                ans[i] = nums[nums[i]];
            }
            return ans;
        }
    };
    

    消灭怪物的最大数量

    (n) 个怪物,每个怪物距离你 (dleft[i ight]),每个怪物的速度为 (sleft[i ight]),怪物们从第 (0) 分钟开始向你移动

    你每分钟能且仅能杀死一个怪物,请返回所有怪物到达你的位置时,你最多杀死的怪物数,如果你能杀死所有怪物,那么返回 (n)

    例如 d = [1,3,4], s = [1,1,1],你可以杀死所有的怪物

    例如 d = [1,1,2,3], s = [1,1,1,1],你只能杀死一个怪物

    例如 d = [3,2,4], s = [5,3,2],你只能杀死一个怪物

    题解

    每个怪物到达你的位置所需要的时间为 (lceil frac{dleft[i ight]}{sleft[i ight]} ceil)

    将所有怪物按照到达的时间存在数组 (t) 中,并从小到大排序

    我们可以在第 (i) 时刻杀排序后的第 (i) 个怪物,如果 (i + 1geq tleft[i ight]),那么这个怪物以及之后的所有怪物都无法杀死,因此我们统计一下数组 (t) 的下标和值的关系即可

    向上取整 (lceil frac{a}{b} ceil)cpp 中的实现为 (a + b - 1) / b

    时间复杂度 (O(n log n))

    class Solution {
    public:
        int eliminateMaximum(vector<int>& d, vector<int>& s) {
            int n = d.size();
            vector<int> t(n);
            for (int i = 0; i < n; ++i) t[i] = (d[i] + s[i] - 1) / s[i];
            sort(t.begin(), t.end());
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (t[i] >= i + 1) ans++;
                else break;
            }
            return ans;
        }
    };
    

    统计好数字的数目

    对于下标从 (0) 开始的数字 (num),如果其偶数下标为偶数 ((0,2,4,6,8)),奇数下标为质数 ((2,3,5,7)),那么我们称之为好数字

    现在给定正整数 (n),要求返回长度为 (n) 的好数字的数量,答案对 (10^9+7) 取模

    数据规定 (1leq nleq 10^{15})

    题解

    根据乘法原理,我们只要统计出偶数下标的数量 (a),以及奇数下标的数量 (b),便可以求出答案,即

    [5^acdot 4^bmod 10^9+7 ]

    显而易见

    [a = lceilfrac{n}{2} ceil, b = lfloorfrac{n}{2} floor ]

    计算答案时使用快速幂即可,时间复杂度为 (O(log^2 n))

    typedef long long LL;
    const int MOD = 1e9 + 7;
    LL qpow(LL a, LL b)
    {
        LL ans = 1;
        while (b) {
            if (b & 1) ans *= a, ans %= MOD;
            a *= a, a %= MOD, b >>= 1;
        }
        return ans;
    }
    class Solution {
    public:
        int countGoodNumbers(long long n) {
            LL ans = qpow(4, n / 2) * qpow(5, (n + 1)/ 2) % MOD;
            return ans;
        }
    };
    

    最长公共子路径

    计算 (n) 个字符串 (s_{i}) 的最长公共子串

    (1leq nleq 10^5)

    (1leq sumlimits_{i = 1}^{n}len(s_{i})leq 10^5)

    题解

    在后缀数组上二分答案

    当然,后缀树和后缀自动机也都是可以的

    对后缀数组不熟悉的同学可以直接跳过,个人感觉后缀数组还是相当难理解的

    const int MAXL = 2e5 + 7, MAXN = 2e5 + 7;
    
    struct SuffixArray {
        struct RadixElement {
            int id, k[2];
        } RE[MAXL], RT[MAXL];
        int N, A[MAXL], SA[MAXL], Rank[MAXL], Height[MAXL], C[MAXL];
        void RadixSort()
        {
            int i, y;
            for (y = 1; y >= 0; y--) {
                memset(C, 0, sizeof(C));
                for (i = 1; i <= N; i++) C[RE[i].k[y]]++;
                for (i = 1; i < MAXL; i++) C[i] += C[i - 1];
                for (i = N; i >= 1; i--) RT[C[RE[i].k[y]]--] = RE[i];
                for (i = 1; i <= N; i++) RE[i] = RT[i];
            }
            for (i = 1; i <= N; i++) {
                Rank[RE[i].id] = Rank[RE[i - 1].id];
                if (RE[i].k[0] != RE[i - 1].k[0] || RE[i].k[1] != RE[i - 1].k[1]) Rank[RE[i].id]++;
            }
        }
        void CalcSA()
        {
            int i, k;
            RE[0].k[0] = -1;
            for (i = 1; i <= N; i++) RE[i].id = i, RE[i].k[0] = A[i], RE[i].k[1] = 0;
            RadixSort();
            for (k = 1; k + 1 <= N; k *= 2) {
                for (i = 1; i <= N; i++) RE[i].id = i, RE[i].k[0] = Rank[i], RE[i].k[1] = i + k <= N ? Rank[i + k] : 0;
                RadixSort();
            }
            for (i = 1; i <= N; i++) SA[Rank[i]] = i;
        }
        void CalcHeight()
        {
            int i, k, h = 0;
            for (i = 1; i <= N; i++) {
                if (Rank[i] == 1)
                    h = 0;
                else {
                    k = SA[Rank[i] - 1];
                    if (--h < 0) h = 0;
                    for (; A[i + h] == A[k + h]; h++);
                }
                Height[Rank[i]] = h;
            }
        }
    } SA;
    int N, Ans, Bel[MAXL];
    char S[MAXL];
    void init(vector< vector< int > >& vec)
    {
        int i;
        N = vec.size();
        SA.N = 0;
        for (i = 0; i < N; i++) {
            for (int j = 0; j < vec[i].size(); ++j) {
                SA.A[++SA.N] = vec[i][j] + '0' + 1;
                Bel[SA.N] = i + 1;
            }
            if (i + 1 < N) SA.A[++SA.N] = 30 + i + 1;
        }
    }
    bool check(int A)
    {
        int i, j, k;
        bool ba[MAXN];
        for (i = 1; i <= SA.N; i++) {
            if (SA.Height[i] >= A) {
                for (j = i; SA.Height[j] >= A && j <= SA.N; j++)
                    ;
                j--;
                memset(ba, 0, sizeof(ba));
                for (k = i - 1; k <= j; k++) ba[Bel[SA.SA[k]]] = true;
                for (k = 1; ba[k] && k <= N; k++)
                    ;
                if (k == N + 1) return true;
                i = j;
            }
        }
        return false;
    }
    void solve()
    {
        int a, b, m;
        SA.CalcSA();
        SA.CalcHeight();
        a = 0;
        b = SA.N;
        while (a + 1 < b) {
            m = (a + b) / 2;
            if (check(m))
                a = m;
            else
                b = m - 1;
        }
        if (check(b)) a = b;
        Ans = a;
    }
    
    class Solution {
    public:
        int longestCommonSubpath(int n, vector< vector< int > >& paths)
        {
            init(paths);
            solve();
            return Ans;
        }
    };
    
  • 相关阅读:
    384. 最长无重复字符的子串
    406. 和大于S的最小子数组
    159. 寻找旋转排序数组中的最小值
    62. 搜索旋转排序数组
    20. 骰子求和
    125. 背包问题 II
    92. 背包问题
    1295. 质因数统计
    471. 最高频的K个单词
    1339. 最大区间
  • 原文地址:https://www.cnblogs.com/ChenyangXu/p/15059226.html
Copyright © 2011-2022 走看看