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;
}

浙公网安备 33010602011771号