CF1418G Three Occurrences
统计满足某些性质的区间个数。
我们考虑移动 \(r\) 指针。
然后考虑把不能选的区间 \(ban\)掉。
具体看下细节吧。
#include<iostream>
#include<cstdio>
#define ll long long
#define N 500005
int n,c[N],u[N],pre[N];
struct P{
int tag,sum;
}T[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
#define t(x) T[x].tag
#define s(x) T[x].sum
#define mid ((l + r) >> 1)
inline void build(int u,int l,int r){
t(u) = 0;
s(u) = r - l + 1;
if(l == r)
return ;
build(ls(u),l,mid);
build(rs(u),mid + 1,r);
}
ll ans;
#define root 1,1,n
ll L;
inline void up(int x,int l,int r){if(t(u))s(u) = 0;else if(l == r) s(u) = 1;else s(u) = s(ls(u)) + s(rs(u));}
inline void change(int u,int l,int r,int tl,int tr,int p){
if(L >= l && R <= r)t(u) += p,up(x,l,r);
else{
if(mid >= tl)
change(ls(u),l,mid,tl,tr,p);
if(mid < tr)
change(rs(u),mid + 1,r,tl,tr,p);
}
up(x,l,r);
}
inline ll find(int u,int l,int r,int tl,int tr){
if(t(u))return 0;
if(tl <= l && r <= tr)
return s(u);
ll ans = 0;
if(tl <= mid)
ans = ans + find(ls(u),l,mid,tl,tr);
if(tr > mid)
ans = ans + find(rs(u),mid + 1,r,tl,tr);
return ans;
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)
scanf("%d",&u[i]),pre[i] = c[u[i]],c[u[i]] = i;
for(int i = 1;i <= n;++i){
if(!pre[i])change(root,1,i,1);
if(!pre[pre[i]])change(root,1,pre[i],-1),change(root,1,i,1);
if(!pre[pre[pre[i]]])change(root,1,pre[i],-1),change(root,pre[pre[i]] + 1,i,1);
else{
change(root,pre[pre[pre[i]]] + 1,pre[i],-1);
change(1,pre[pre[i]] + 1,i,1);
}
L = std::max(L,pre[pre[pre[i]]] + 1);
ans += find(root,L,i);
}
std::cout<<ans<<std::endl;
}