题解:CF1418G Three Occurrences

想了2.5h。用线段树扫描线维护对每个 \(l\) ,每个数字是否合法(出现 \(0\)\(3\) 次)的个数,扫描 \(r\) 统计合法的 \(l,r\) 区间(次数 \(=n\) 的个数)。对位置数字和前面连续的非本数字区间进行区间处理即可。

#include<iostream>
using namespace std;
const int N=5e5+10;
#define MID int mid=(r-l)/2+l
int n,pre[N][4];
long long ans;
struct seg{
	int mx,cnt,tag;
	void pushup(const seg &ls,const seg &rs){
		if(ls.mx==rs.mx) mx=ls.mx,cnt=ls.cnt+rs.cnt;
		else if(ls.mx>rs.mx) mx=ls.mx,cnt=ls.cnt;
		else mx=rs.mx,cnt=rs.cnt;
	}
	void pushdown(seg &ls,seg &rs){
		ls.mx+=tag,rs.mx+=tag;
		ls.tag+=tag,rs.tag+=tag; 
		tag=0;
	}
	seg(){
		mx=cnt=tag=0;
	}
}tr[N*4];
void build(int u,int l,int r){
	if(l==r) return(void)(tr[u].mx=n,tr[u].cnt=1);
	MID;
	build(u*2,l,mid),build(u*2+1,mid+1,r);
	tr[u].pushup(tr[u*2],tr[u*2+1]);
}
void upd(int u,int l,int r,int L,int R,int add){
	if(l>R||r<L||R<L) return;
	if(L<=l&&r<=R) return (void)(tr[u].mx+=add,tr[u].tag+=add);
	tr[u].pushdown(tr[u*2],tr[u*2+1]);
	MID;
	upd(u*2,l,mid,L,R,add),upd(u*2+1,mid+1,r,L,R,add);
	tr[u].pushup(tr[u*2],tr[u*2+1]);
}
seg query(int u,int l,int r,int L,int R){
	seg tmp;
	if(l>R||r<L) return tmp;
	if(L<=l&&r<=R) return tr[u];
	tr[u].pushdown(tr[u*2],tr[u*2+1]);
	MID;
	tmp.pushup(query(u*2,l,mid,L,R),query(u*2+1,mid+1,r,L,R));
	return tmp;
}
int main(){
	cin>>n;
	build(1,1,n);
	for(int i=1;i<=n;i++){
		int a;
		cin>>a;
		upd(1,1,n,pre[a][0]+1,i,-1);
		if(pre[a][1]){
			upd(1,1,n,pre[a][2]+1,pre[a][1],1);
			upd(1,1,n,pre[a][3]+1,pre[a][2],-1);
		}
		pre[a][3]=pre[a][2],pre[a][2]=pre[a][1],pre[a][1]=pre[a][0],pre[a][0]=i;
		seg tmp=query(1,1,n,1,i);
		if(tmp.mx==n) ans+=tmp.cnt;
	}
	cout<<ans<<endl;
	return 0;
}

注意到洛谷题解区有一篇思路一模一样,懊恼。

posted @ 2026-07-15 11:08  zmdayo  阅读(6)  评论(0)    收藏  举报