[GKS 2019 #B] Diverse Subarray 题解

· · 题解

[GKS 2019 #B] Diverse Subarray 题解

思路

本题要求选一个连续区间 [l, r],使得其中不会被扔掉的饰品数量最多。规则是:若某类型在区间内出现次数超过 S,则该类型所有饰品都被扔掉。 直接枚举 l, r 是 O(n^2),配合统计会超时。考虑枚举右端点 r,用线段树维护每个左端点 l 的答案: 线段树位置 l 的值表示:固定右端点为当前 r,左端点取 l 时能保留的饰品数。 每次 r 右移一位,区间 [1, r] 整体 +1(多了一个饰品)。 若当前类型 a[r] 的出现次数达到 S+1,则所有包含这 S+1 个同类型饰品的左端点,该类型会被整类扔掉,需要减去相应贡献。

预处理

用 \operatorname{pos}[v] 记录类型 v 出现的所有位置,\operatorname{now}[i] 表示 a[i] 是第几次出现。 初始时(r=n 时的“基准”),构造数组 \operatorname{in}[i]: 若 a[i] 是第 S+1 次出现,它对所有左端点的贡献为 -(S+1)(整类被扔); 若是第 >S+1 次出现,贡献为 -1(多一个被扔的); 否则贡献为 +1。 用前缀和 O(n) 建出线段树。

枚举右端点

从 i=1 到 n 枚举当前右端点 r=i: 先查询 \operatorname{ask}(1, n, 1, i, n),即左端点 \le i 时的最大答案,更新全局答案。 再更新线段树,让下一轮(r=i+1)的值正确: 若后面还有至少 S 个同类型(位置 \operatorname{tmp} 和 \operatorname{tmp2}),则:

$[\operatorname{tmp}, \operatorname{tmp2}-1]$ 加 $S$:数量从 $S+1$ 变成 $S+2$,被扔的该类型多一个,但区间长度也 $+1$,净增 $S$。 否则 $[i, n]$ 全部减 $1$。 ## 时间复杂度 $O(n\log n)$。 ## 代码 ```cpp #include <bits/stdc++.h> using namespace std; #define int long long int n,m; int a[1000005]; vector<int> pos[1000005]; int now[1000005],in[1000005]; struct node { int l,r,mx,add; }t[4000005]; void downdate(int p) { if(t[p].add) { t[p*2].mx+=t[p].add; t[p*2+1].mx+=t[p].add; t[p*2].add+=t[p].add; t[p*2+1].add+=t[p].add; t[p].add=0; } } void build(int p,int l,int r) { t[p].l=l,t[p].r=r; t[p].add=0; if(l==r) {t[p].mx=in[l];return;} int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); t[p].mx=max(t[p*2].mx,t[p*2+1].mx); } void update(int l,int r,int p,int st,int ed,int k) { if(st<=l&&ed>=r) { t[p].mx+=k; t[p].add+=k; return; } downdate(p); int mid=(l+r)/2; if(st<=mid) update(l,mid,p*2,st,ed,k); if(ed>mid) update(mid+1,r,p*2+1,st,ed,k); t[p].mx=max(t[p*2].mx,t[p*2+1].mx); } int ask(int l,int r,int p,int st,int ed) { if(st<=l&&ed>=r) return t[p].mx; downdate(p); int val=0; int mid=(l+r)/2; if(st<=mid) val=max(val,ask(l,mid,p*2,st,ed)); if(ed>mid) val=max(val,ask(mid+1,r,p*2+1,st,ed)); return val; } signed main() { ios::sync_with_stdio(0);cin.tie(0); int T; cin>>T; for(int cas=1;cas<=T;cas++) { cin>>n>>m; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=1000000;i++)pos[i].clear(); for(int i=1;i<=n;i++) { pos[a[i]].push_back(i); now[i]=pos[a[i]].size(); } for(int i=1;i<=1000000;i++)pos[i].push_back(n+1); in[0]=0; for(int i=1;i<=n;i++) { in[i]=in[i-1]+1; if(now[i]==m+1) in[i]-=m+1; else if(now[i]>m+1) in[i]--; } build(1,1,n); int ans=0; for(int i=1;i<=n;i++) { ans=max(ans,ask(1,n,1,i,n)); int num=a[i]; if(now[i]+m<(int)pos[num].size()) { int tmp=pos[num][now[i]+m-1]; int tmp2=pos[num][now[i]+m]; update(1,n,1,i,tmp-1,-1); update(1,n,1,tmp,tmp2-1,m); } else update(1,n,1,i,n,-1); } cout<<"Case #"<<cas<<": "<<ans<<"\n"; } return 0; } ```