浙农大第十九届程序设计竞赛 G-最优的连续子段
最优的连续子段
https://ac.nowcoder.com/acm/contest/7872/G
分析
因为是区间问题,我们先选择一个定点,即枚举右端点r,那么我们要求的就是左端点在[1,r],出现次数为1的数字最大个数。具体看图
假设当前已确定右端点r,且此时 ,可以确定,当左端点 时,这些区间中出现次数为1的数字的个数都会加1,而小于等于j的,就会全部减1
那为什么减一的区间是 ,明显,当右端点是j的时候,区间 已经被减过一次,自然不能再减
代码
#include<bits/stdc++.h> #define ls now<<1 #define rs now<<1|1 using namespace std; const int N=1e5+10; int n; int a[N],nex[N],s[N<<2],tag[N<<2],las[N]; inline void pd(int now) { if(!tag[now]) return ; tag[ls]+=tag[now];s[ls]+=tag[now]; tag[rs]+=tag[now];s[rs]+=tag[now]; tag[now]=0; return ; } inline void add(int now,int l,int r,int x,int y,int z) { if(r<x||l>y) return ;if(x<0||y>n) return ; if(l>=x&&r<=y) { s[now]+=z,tag[now]+=z; return ; }pd(now); int mid=(l+r)>>1; if(x<=mid) add(ls,l,mid,x,y,z); if(mid<y) add(rs,mid+1,r,x,y,z); s[now]=max(s[ls],s[rs]); } inline int find(int now,int l,int r,int x,int y) { if(l>=x&&r<=y) return s[now]; pd(now); int mid=(l+r)>>1,ret=0; if(x<=mid) ret=find(ls,l,mid,x,y); if(mid<y) ret=max(ret,find(rs,mid+1,r,x,y)); return ret; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=0; for (int i=1;i<=n;i++) { las[i]=nex[a[i]];nex[a[i]]=i; add(1,1,n,las[i]+1,i,1); add(1,1,n,las[las[i]]+1,las[i],-1); ans=max(ans,find(1,1,n,1,i)); } printf("%d\n",ans); return 0; }