题解:P17234 [Algo Beat Contest 017 C] 交互题
关于某人在一道黄题上 all in 两个小时……
正解
考虑我们要找的区间
很自然会有初步想法:当前
然后考虑如何在最小区间的基础上扩展统计答案,因为最小区间的选取同时也保证了在其补区间内不会出现
查找可以用二分简单实现,也可以选择每次直接遍历对应的位置数组“暴力”找,因为最小区间一直在单调扩展且每个位置只会被遍历一次。
注意:若
l_k 或r_k 不存在,说明可以取的范围有l \in [1,l_0], r \in [r_0,n] ,为了贴合上述的答案计算形式也可以写成l \in (0,l_0], r \in [r_0,n+1) ,即l_k=0,r_k=n+1 。
根据性质,我们发现当
特殊地,当
注意如果
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
int n, a[maxn];
vector<int> pos[maxn]; // 统计每个值出现的位置
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> a[i]; pos[a[i]].push_back(i); // 预处理位置
}
for(int i = 0; i <= 2e5; i ++){
sort(pos[i].begin(), pos[i].end()); // 排序
}
if(pos[0].empty()){ // 特判
cout << 0; return ;
}
// 统计k=0的答案
ll ans = 0;
for(int i = 0; i < (int)pos[0].size()-1; i ++){
int s = pos[0][i+1] - pos[0][i] - 1; // 统计非0元素个数
ans += 1LL*s*(s+1)/2;
}
// 注意不要漏下[1,pos0[0]]、[pos0[sz-1],n]两个周边没有0包裹的段
if(1 < pos[0][0]){
ans += 1LL*pos[0][0]*(pos[0][0]-1)/2;
}
if(pos[0].back() < n){
int s = n - pos[0].back();
ans += 1LL*s*(s+1)/2;
}
int l0 = pos[0][0], r0 = pos[0].back();
for(int k = 1, lk, rk; k < maxn; k ++){
if(pos[k].empty()) break;
// 确定lk,rk
auto it = lower_bound(pos[k].begin(), pos[k].end(), l0);
if(it!=pos[k].begin()) lk=*prev(it);
else lk=0;
it = upper_bound(pos[k].begin(), pos[k].end(), r0);
if(it!=pos[k].end()) rk=*it;
else rk=n+1;
// 检查[l0,r0]中是否存在k
it = lower_bound(pos[k].begin(), pos[k].end(), l0);
if(it==pos[k].end() || *it>r0){
ans += 1LL*(l0-lk)*(rk-r0);
}
// 扩展l0,r0
l0 = min(l0, pos[k][0]);
r0 = max(r0, pos[k].back());
}
cout << ans << '\n';
}
signed main()
{
ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}
::::