#4616. 时间复杂度
题目描述
在你的帮助下,小凯成功找到了宝藏价值最大的方案。接下来他在闲逛时被一个游戏机吸引了。
游戏机中共有 $n$ 个带颜色的小球,第 $i$ 个小球的颜色是 $a_i$ 。小凯需要选出一个区间(假设长度为 $l$),满足对于任意颜色的小球:
- 要么在这个区间中出现 $0$次(即不出现);
- 要么在这个区间中出现次数 $\ge b_l$。
其中 $b_l$ 是一个给定的数组,且满足对于任意 $i<j$ 有 $b_i \ge b_j$。如果最终小凯选出的区间越长,获得的奖励就越大。
小凯想用 OI 知识解决这个问题,但是由于水平问题,他只会 $O(n^3)$ ,而游戏机中的球非常多。因此他又来厚颜无耻地找你,希望你能帮他优化时间复杂度,解决这个问题。
即求出,最长满足要求的区间长度是多少。特别的,若问题无解,输出 $0$ 。
数据范围
对于 100% 的数据, $n \le 10^6, 0 \le a_i \le n, 1 \le b_i \le n+1$, 且满足对于任意,$i < j$ 有 $b_i \ge b_j$
题解
考场只会暴力选手
考虑到一个区间如果不合法,那么这个区间的子区间可能成为合法答案当且仅当这个子区间不包含这个区间内颜色出现次数小于 $b$ 的颜色
基于这个思想,我们可以进行启发式分裂。对于 $[L,R]$ ,拿 $l$ 指针从左到右扫描, $r$ 指针从右到左扫描,当遇到第一个颜色在这个区间内出现个数小于 $b$ 的,那我们肯定不要这个点,就相当于把区间分裂成两个子区间再去得到答案,注意要用小区间更新大区间即可。复杂度: $O(nlogn)$
代码
#include <bits/stdc++.h> using namespace std; const int N=1e6+5; int n,a[N],b[N],c[N]; int solve(int L,int R){ int l=L,r=R,e=r-l+1,g=0; while(l<=r){ if (c[a[l]]<b[e]){ for (int i=L;i<=l;i++) c[a[i]]--; if (l+1<=R) g=solve(l+1,R); for (int i=L;i<l;i++) c[a[i]]=0; for (int i=L;i<l;i++) c[a[i]]++; if (L<=l-1) g=max(g,solve(L,l-1)); return g; } if (c[a[r]]<b[e]){ for (int i=r;i<=R;i++) c[a[i]]--; if (L<=r-1) g=solve(L,r-1); for (int i=R;i>r;i--) c[a[i]]=0; for (int i=R;i>r;i--) c[a[i]]++; if (r+1<=R) g=max(g,solve(r+1,R)); return g; } l++;r--; } return e; } int main(){ cin>>n; for (int i=1;i<=n;i++) scanf("%d",&a[i]),c[a[i]]++; for (int i=1;i<=n;i++) scanf("%d",&b[i]); printf("%d\n",solve(1,n));return 0; }