CF1842C
题目描述
共有 t t t 组测试样例。
对于每组测试样例,有一个大小为 n n n 的数组 a a a。你可以进行下列一组操作任意多次:
- 选择 i i i 和 j j j,使 1 ≤ i < j ≤ ∣ a ∣ 1\leq i \lt j \leq |a| 1≤i<j≤∣a∣ 并且 a i = a j a_i=a_j ai=aj,
- 从数组中删除 a i , a i + 1 , … , a j a_i,a_{i+1},…,a_{j} ai,ai+1,…,aj(并将 a j a_j aj 右边的所有元素的索引减少 j − i + 1 j−i+1 j−i+1)。
求出能删除数的最大数量。
注: ∣ a ∣ |a| ∣a∣ 表示 a a a 数组的大小。
数据范围
1 ≤ t ≤ 1 0 3 1\leq t\leq 10^3 1≤t≤103
1 ≤ n ≤ 2 ⋅ 1 0 5 1\leq n\leq 2\cdot 10^5 1≤n≤2⋅105
1 ≤ a i ≤ n 1\leq a_i \leq n 1≤ai≤n
保证所有测试样例的 n n n 总和不超过 2 ⋅ 1 0 5 2\cdot 10^5 2⋅105 。
思路
对于四个数组下标 a , b , c , d a,b,c,d a,b,c,d 满足 a < b < c < d a<b<c<d a<b<c<d :
- 首先,不可能同时删除范围 ( a , c ) (a,c) (a,c) 和 ( b , d ) (b,d) (b,d) 。
- 其次,如果我们删除范围 ( a , d ) (a,d) (a,d) 和 ( b , c ) (b,c) (b,c) ,我们必须先删除范围 ( b , c ) (b,c) (b,c) ,然后再删除范围 ( a , d ) (a,d) (a,d) 。然而,先删除范围 ( b , c ) (b,c) (b,c) 再删除 ( a , d ) (a,d) (a,d) 与只删除 ( a , d ) (a,d) (a,d) 的效果是一样的。
因此,我们可以假设,在最优解中,删除的范围都是不相交的。题目就转化为:希望找到某几个不相交的范围 [ l 1 , r 1 ] , [ l 2 , r 2 ] , … , [ l m , r m ] [l_1,r_1],[l_2,r_2],\ldots,[l_m,r_m] [l1,r1],[l2,r2],…,[lm,rm] ,使得 a l i = a r i a_{l_i}=a_{r_i} ali=ari 且 ∑ i = 1 m ( r i − l i + 1 ) \sum_{i=1}^m (r_i-l_i+1) ∑i=1m(ri−li+1) 为最大值。
我们可以写一个 DP 。 d p i dp_i dpi 表示数组 a a a 前 i i i 位删除的最大球数。
可以 O ( n 2 ) O(n^2) O(n2) 枚举由第 j j j 位转移,得到转移方程:
d p i = max ( d p i − 1 , d p j − 1 + i − j + 1 ) dp_i=\max(dp_{i-1},dp_{j-1}+i-j+1) dpi=max(dpi−1,dpj−1+i−j+1)
考虑优化 DP 。观察到 DP 转移方程中的未知量仅有 d p j − 1 − j + 1 dp_{j-1}-j+1 dpj−1−j+1,由于 1 ≤ a i ≤ n 1\leq a_i\leq n 1≤ai≤n ,可以使用一个桶数组 s s s 记录对于数字 x x x ,满足 a j = x a_j=x aj=x 最大的 d p j − 1 − j + 1 dp_{j-1}-j+1 dpj−1−j+1 。
注意: s [ i ] s[i] s[i] 为负数,初始化需设为极小值。
这样就将 DP 转移方程优化到了 O ( n ) O(n) O(n) :
d p i = max ( d p i − 1 , s a i + i ) s a i = max ( s a i , d p i − 1 − i + 1 ) dp_i=\max(dp_{i-1},s_{a_i}+i)\\ s_{a_i}=\max(s_{a_i},dp_{i-1}-i+1) dpi=max(dpi−1,sai+i)sai=max(sai,dpi−1−i+1)
代码
#include<bits/stdc++.h>
using namespace std;
int t,n;
int a[200010],dp[200010],s[200010];
int main()
{
scanf("%d",&t);
while(t--)
{
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
s[i]=-1e9;//初始化为极小值
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
dp[i]=max(dp[i-1],s[a[i]]+i);//DP转移方程
s[a[i]]=max(s[a[i]],dp[i-1]-i+1);
}
printf("%d\n",dp[n]);
}
return 0 ;
}