CF467E 题解
为方便起见,把数叫做颜色。把 \(a_i,a_j,a_k,a_w(a_i=a_k,a_j=a_w,i<j<k<w)\) 加入 \(b\) 中叫做一次加入。
注意到如果从左到右扫描时,某四个数符合要求,就可以直接加入,这样一定比把某种颜色留到后面用更优。
因为这四个数留到后面最多只能贡献给另外一次的加入。答案一定不优。
于是可以直接贪心。
考虑一次加入的四个数 \(x_1\ y_1\ x_2\ y_2(x_1=x_2,y_1=y_2)\),当扫描到 \(x_2\) 时,我们就可以给每个满足条件的 \(y_1\) 的颜色打上标记。
当扫描到的位置 \(p\) 的颜色被打上标记时,说明存在一次加入,且 \(y_2=a_p\)。
哪些 \(y_1\) 满足条件呢?
设当前扫描到的位置为 \(i\)(令 \(x_2\) 的颜色为 \(a_i\))。
考虑维护一个栈 \(S\) 作为可能的 \(y_1\) 集合,再维护 \(cnt_j\) 表示 \(k\in{S},a_k=j\) 的 \(k\) 的个数。
如果栈顶的颜色不为 \(a_i\),且 \(cnt_{a_i}\geq 1\)(存在 \(x_1\)),则有可能为 \(y_1\)。
如果栈顶颜色等于 \(a_i\),说明可能 \(x_1=y_1=x_2=y_2\),那么当且仅当 \(cnt_{a_i}>1\)(存在两个相同颜色,同时作为 \(x_1,y_1\))。
否则不可能。
如果栈顶可能成为 \(y_1\),就可以弹出了。否则结束判断,更新 \(cnt_{a_i}\),扫描下一个。
如果扫描到被打上标记的,那么加入答案,并且前面的标记全部作废。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
int n, a[N];
unordered_map<int, int> pre, cnt;
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
stack<int> s;
vector<int> ans;
for(int i = 1; i <= n; i ++)
{
if(pre.count(a[i]))
{
ans.push_back(pre[a[i]]);
ans.push_back(a[i]);
ans.push_back(pre[a[i]]);
ans.push_back(a[i]);
pre.clear(), cnt.clear();
continue;
}
while(s.size() && (cnt[a[i]] > 1 || (cnt[a[i]] == 1 && s.top() != a[i])))
{
pre[s.top()] = a[i];
cnt[s.top()] --;
s.pop();
}
cnt[a[i]] ++;
s.push(a[i]);
}
cout << ans.size() << "\n";
for(int i : ans) cout << i << " ";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】