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;
}   
posted @ 2021-10-18 21:35  fhq_treap  阅读(45)  评论(0编辑  收藏  举报