题解:P17234 [Algo Beat Contest 017 C] 交互题
oi_tuanzhang
·
·
题解
P17234 [Algo Beat Contest 017 C] 交互题
先将原序列的 mex 算出来,发现若为 0 则答案也为 0。
枚举同时为 mex 与 cmin 的 x。
考虑什么样的区间可使 x 同时为 mex 和 cmin,发现对于区间 [l,r],其需满足 [l,r] 内无 x,保证 [0,x-1] 只出现在其中,也就是 <x 的都在,>x 的数随意。
发现前者是好维护的,考虑倒序枚举跑双指针,左右端点分别维护第一个、最后一个 <x 的数的位置,显然对于减小的 x,其是不断向内收缩的,那么这个指针对应的区间 [l',r'] 就满足所有 <x 的数都在。
但保证 [0,x-1] 都在就比较难维护,考虑记 lst_i 为 i 在序列中第一次出现的位置,初始化 lst_i = -inf,维护一个 mn_i = \min_{j=0}^{i}lst_j,表示所有 \le i 的数出现的最左端。
则发现在判断区间 [l',r'] 是否包含所有 [0,x-1] 时,只需判断是否有 mn_{x-1} \ge l 即可,因为若没全出现会有 mn_{x-1} = -inf(不知道有没有更好的方法,感觉用主席树搞这个有点大炮打蚊子的感觉),对于不合法的直接跳过即可,下面只考虑可找出合法区间的 x。
先对 a_i 维护其每一个出现位置,令使 x 目前合法的区间为 [ls,rs](只未判断 x 是否出现的区间),还需进行判断 x 是否出现,对于合法情况考虑计算贡献:
1、x 只出现一次,判断其在 ls 左,rs 右,还是中间即可,最后这种是不合法的,前两种计算贡献是平凡的。
2、x 出现多次,判断其全在 ls 左,全在 rs 右,或是存在交叉。前两种情况是简单的,考虑最后的情况。使用 lower_bound 和 upper_bound 找出 x 在 ls 左和 rs 右第一次出现的位置 p,q,发现若找不到 p,q 或者 p,q 两位置在 x 的出现序列中不相邻,这些情况都代表着 [ls,rs] 中至少出现了一次 x,属于不合法情况。那么对于最终的合法的 p,q,其对答案贡献为 (ls-p) \times (q-rs)。
```cpp
#include<bits/stdc++.h>
#define pii pair<int,int>
#define lowbit(x) (x&(-x))
#define int long long
#define inf 1000000000000000000ll
using namespace std;
const int Maxn=2e5+5;
int n,mex,a[Maxn],lst[Maxn],mn[Maxn];
bool vis[Maxn];
vector<int> e[Maxn];
int read()
{
int k=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9')k=k*10+c-'0',c=getchar();
return k*f;
}
void out(int x)
{
if(x<0)putchar('-'),x=-x;
if(x<10)putchar(x+'0');
else out(x/10),putchar(x%10+'0');
}
int cal(int x)
{
return x*(x+1)/2;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
int mx=-inf;
for(int i=1;i<=n;i++)
{
cin>>a[i];
mx=max(mx,a[i]);
vis[a[i]]=1;
lst[a[i]]=-inf;
e[a[i]].push_back(i);
}
e[0].push_back(n+1);
while(mex<=mx&&vis[mex]) mex++;
if(!mex)
{
cout<<0<<'\n';
return 0;
}
int l=1,r=n;
int ans=0;
for(int i=1;i<=n;i++)
if(lst[a[i]]==-inf) lst[a[i]]=i;
mn[0]=lst[0];
for(int i=1;i<=mx;i++) mn[i]=min(mn[i-1],lst[i]);
for(int i=mex-1;i>=1;i--)
{
while(a[l]>=i&&l<r) l++;
while(a[r]>=i&&r>l) r--;
if(l==r&&a[l]>=i) break;
// cout<<"??? "<<i<<" "<<l<<" "<<r<<'\n';
if(mn[i-1]<l) continue;
if((int)e[i].size()==1)
{
if(e[i][0]>r)
ans+=(e[i][0]-r)*l;
else if(e[i][0]<l)
ans+=(l-e[i][0])*(n-r+1);
}
else
{
int len=(int)e[i].size()-1;
if(e[i][len]<l) ans+=(n-r+1)*(l-e[i][len]);
else if(e[i][0]>r) ans+=l*(e[i][0]-r);
else
{
auto it1=lower_bound(e[i].begin(),e[i].end(),l);
auto it2=upper_bound(e[i].begin(),e[i].end(),r);
if(it1==e[i].begin()) continue;
if(it2==e[i].end()) continue;
it1=prev(it1);
if(it1+1!=it2) continue;
int ls=*it1,rs=*it2;
ans+=(l-ls)*(rs-r);
}
}
}
// cout<<"!!! "<<ans<<'\n';
int pre=0;
for(auto p:e[0])
{
ans+=cal(p-pre-1);
pre=p;
}
cout<<ans<<'\n';
return 0;
}
```