1 条题解
-
0
自动搬运
来自洛谷,原作者为

船酱魔王
如花美眷,似水流年搬运于
2025-08-24 22:49:42,当前版本为作者最后更新于2023-05-15 22:09:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Colorful Days♪ 官方题解
题意回顾
给定长度为 的数组 和长度为 的数组 。
定义 为 数组后拼接 数组,定义 (即空数组), ,有 。
定义 为 数组和 数组的最长公共子序列长度。
求出 使得 取到最大,在此基础上 最小,并按照规定要求输出。
,,。
分析
输出
0 0可得 2 分。解决第一问(即 sub4)可再得 15 分,先考虑第一问解法,即不择手段地构造最长 。
可以发现,当一个数不在 中出现,他一定不会存在在 中。
设有 个 的位置使得该位置数字在 中出现,记为 的合法位置,则复制 ,复制 次。可以在每次 循环一遍时,第 次可以选出第 个 的合法位置。
因此,第一问答案即为 。
我们记 为将 中合法位置依次串联组成的新数组。
第二问即要求在 的不断重复中找到 作为子序列。
可以贪心的按照 中的数依次找,每次找到这个数在上一个数出现后第一次出现在 的不断循环中的位置。
每次暴力去找的时间复杂度最坏是 的,因为捆绑了测试点且数据经过特殊构造只能拿到 sub2~3 的分数。因此考虑快速去找。
我们开辟动态数组 , 表示数字 在 中的出现的所有位置。
维护变量 每次找一个位置之后的字符时只需要在新字符的数组中二分大于 (当前位置)的出现位置,如果找不到将 加一, 更新为新字符第一次的出现位置。
时间复杂度 。
AC 代码
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N = 1e6 + 5; int n, m, c1, c2; int s[N]; int t[N]; vector<int> g[N]; int findx(int p, int val) { int l, r, mid; l = -1; r = g[p].size(); while(l + 1 < r) { mid = (l + r) >> 1; if(g[p][mid] > val) { r = mid; } else { l = mid; } } return r; } bool vis[N]; int main() { scanf("%d%d%d%d", &n, &m, &c1, &c2); for(int i = 1; i <= n; i++) { scanf("%d", &s[i]); vis[s[i]] = 1; } for(int i = 1; i <= m; i++) { scanf("%d", &t[i]); } for(int i = 1; i <= m; i++) { if(!vis[t[i]]) { t[i] = -1; } } int m1 = m; m = 0; for(int i = 1; i <= m1; i++) { if(t[i] != -1) { m++; t[m] = t[i]; } } if(m == 0) { printf("0 0\n"); return 0; } for(int i = 1; i <= n; i++) { g[s[i]].push_back(i); } int sc = 1; int pos = g[t[1]][0]; for(int i = 2; i <= m; i++) { pos = findx(t[i], pos); if(pos == g[t[i]].size()) { pos = g[t[i]][0]; sc++; } else { pos = g[t[i]][pos]; } } printf("%d %d\n", m * c1, sc * c2); return 0; }总结与评价
本题用到的算法较为基础,但是考察选手的思维能力。
个人认为下位绿。
- 1
信息
- ID
- 8762
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者