本次周赛前三题不难,最后一题比较有难度
出题人不讲武德,出后缀数据结构来为难大家
涉及知识点:模拟,贪心,排序,快速幂,向上取整,后缀数组
基于排列构建数组
给定一个数组 (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),便可以求出答案,即
显而易见
计算答案时使用快速幂即可,时间复杂度为 (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;
}
};