【HDU6635】Nonsense Time
题目大意:给定一个长度为 N 的序列,开始时所有的位置都不可用,每经过一个时间单位,都会有一个位置被激活。现给定激活顺序的序列,求每次激活之后全局的最长上升子序列的长度,保证数据随机。
题解:
引理:数据随机的情况下,一个长度为 N 的序列的最长上升子序列的期望长度为 O(√n)。(暂时不会证明qaq)
发现若正序考虑,判断每次被激活位置的值是否对当前全局最优解有贡献这一操作所消耗的复杂度过高,无法接受。考虑时间倒流,即:初始时所有位置的值均被激活,每次会有一个位置的值无效。根据引理可知,i 个数字的 LIS 的期望值为 O(√i),即:每 √n 个数字无效时,全局 LIS 才会减少 1。因此,只需要判断每次无效的数是否在全局 LIS 中,若存在,则暴力重构全局最优解,否则直接输出答案即可。时间复杂度为 O(n√nlogn)。
代码如下
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n), melt(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> melt[i];
melt[i]--;
}
vector<bool> frozen(n), book(n);
vector<int> ans(n), dp(n), pos(n);
auto RunLIS = [&]() {
fill(dp.begin(), dp.end(), 1e9);
fill(book.begin(), book.end(), 0);
fill(pos.begin(), pos.end(), -1);
int r = 0;
for (int i = 0; i < n; i++) {
if (frozen[i] == 0) {
int id = lower_bound(dp.begin(), dp.end(), a[i]) - dp.begin();
if (dp[id] == 1e9) {
r = max(r, id);
}
dp[id] = a[i], pos[i] = id;
}
}
for (int i = n - 1, now = 1e9, len = r; i >= 0; i--) {
if (len < 0) break;
if (pos[i] == len && a[i] < now) {
now = a[i];
len--;
book[i] = 1;
}
}
return r + 1;
};
int ret = RunLIS();
for (int i = n - 1; i >= 0; i--) {
ans[i] = ret;
frozen[melt[i]] = 1;
if (book[melt[i]] == 1) {
ret = RunLIS();
}
}
cout << ans[0];
for (int i = 1; i < n; i++) {
cout << " " << ans[i];
}
cout << endl;
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步