G. Three Occurrences(问有多少个子序列(连续)满足所有值只出现确切3次。)
题:https://codeforces.com/contest/1418/problem/G
题意:给定n(n<=5e5)的序列,问有多少个子序列(连续)满足所有值只出现确切3次。
分析:总操作:枚举左端点,查看多少个满足的右端点;
对于某个值,要么不出现,要么只出现3次,那么对于当前枚举的左端点的值a[l],合法区间肯定就是之前累计过的第2次出现到第3次出现的位置之间(不包含第3次出现的位置);
那么设对于每个合法区间的右端点我们标记为0,那么答案就是0的个数;
首先没出现一次都让[i,pos[ a[i] ].size()-1] ]内先假设不合法,那么到第2,第3次的时候加回-1让其为0变合法,统计0最小值个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int M=5e5+6; const int inf=0x3f3f3f3f; const ll INF=1e18; vector<int>pos[M]; int a[M]; struct SegTree{ int tr[M<<2],cnt[M<<2],lz[M<<2]; void up(int root){ tr[root]=min(tr[root<<1],tr[root<<1|1]); cnt[root]=0; if(tr[root]==tr[root<<1]) cnt[root]=cnt[root<<1]; if(tr[root]==tr[root<<1|1]) cnt[root]+=cnt[root<<1|1]; } void pushdown(int root){ if(lz[root]){ tr[root<<1]+=lz[root]; tr[root<<1|1]+=lz[root]; lz[root<<1]+=lz[root]; lz[root<<1|1]+=lz[root]; lz[root]=0; } } void build(int root,int l,int r){ lz[root]=0; if(l==r){ tr[root]=0; cnt[root]=1; return ; } int midd=(l+r)>>1; build(lson); build(rson); up(root); } void update(int L,int R,int val,int root,int l,int r){ if(L<=l&&r<=R){ tr[root]+=val; lz[root]+=val; return ; } int midd=(l+r)>>1; pushdown(root); if(L<=midd) update(L,R,val,lson); if(R>midd) update(L,R,val,rson); up(root); } int query(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ return tr[root]==0 ? cnt[root] : 0; } pushdown(root); int midd=(l+r)>>1; int res=0; if(L<=midd) res=query(L,R,lson); if(R>midd) res+=query(L,R,rson); return res; } }t1; int getid(int u,int x){ return pos[u][pos[u].size()-x]; } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); t1.build(1,1,n); for(int i=1;i<=n;i++) pos[i].pb(n+1); ll ans=0; for(int i=n;i>=1;i--){ ///1位置 if(pos[a[i]].size()>=4){///让之前2位置的合法区间无效 t1.update(getid(a[i],3),getid(a[i],4)-1,1,1,1,n); } pos[a[i]].pb(i); ///2位置 if(pos[a[i]].size()>=4){///抵消掉下方之前位置3的操作,让合法区间有效 t1.update(getid(a[i],3),getid(a[i],4)-1,-1,1,1,n); } /*for(auto it:pos[a[i]]) cout<<it<<' '; cout<<endl;*/ ///3位置 t1.update(i,getid(a[i],2)-1,1,1,1,n);///先假设区间无效 ans+=t1.query(i,n,1,1,n); } printf("%lld\n",ans); return 0; } /*** 10 9 10 9 8 10 9 8 7 10 6 10 6 5 10 9 8 7 4 10 9 8 7 4 3 10 9 8 7 4 3 2 10 6 5 1 ***/