单周赛 248 题解

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

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

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

基于排列构建数组

给定一个数组 \(nums\),构建一个数组 \(ans\),使得 \(ans[i] = nums\left[nums\left[i\right]\right]\)

题解

按照题意写就行

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\) 个怪物,每个怪物距离你 \(d\left[i\right]\),每个怪物的速度为 \(s\left[i\right]\),怪物们从第 \(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{d\left[i\right]}{s\left[i\right]}\rceil\)

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

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

向上取整 \(\lceil \frac{a}{b}\rceil\)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\) 取模

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

题解

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

\[5^a\cdot 4^b\mod 10^9+7 \]

显而易见

\[a = \lceil\frac{n}{2}\rceil,\ b = \lfloor\frac{n}{2}\rfloor \]

计算答案时使用快速幂即可,时间复杂度为 \(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}\) 的最长公共子串

\(1\leq n\leq 10^5\)

\(1\leq \sum\limits_{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;
    }
};
posted @ 2021-07-25 22:12  徐摆渡  阅读(30)  评论(0编辑  收藏  举报