CF1842C题解

CF1842C

Lougu题面

CF Problem

CF Contest

题目描述

共有 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| 1i<ja 并且 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 ji+1)。

求出能删除数的最大数量。

注: ∣ a ∣ |a| a 表示 a a a 数组的大小。

数据范围

1 ≤ t ≤ 1 0 3 1\leq t\leq 10^3 1t103

1 ≤ n ≤ 2 ⋅ 1 0 5 1\leq n\leq 2\cdot 10^5 1n2105

1 ≤ a i ≤ n 1\leq a_i \leq n 1ain

保证所有测试样例的 n n n 总和不超过 2 ⋅ 1 0 5 2\cdot 10^5 2105

思路

对于四个数组下标 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(rili+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(dpi1,dpj1+ij+1)

考虑优化 DP 。观察到 DP 转移方程中的未知量仅有 d p j − 1 − j + 1 dp_{j-1}-j+1 dpj1j+1,由于 1 ≤ a i ≤ n 1\leq a_i\leq n 1ain ,可以使用一个桶数组 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 dpj1j+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(dpi1,sai+i)sai=max(sai,dpi1i+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 ;
}
  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值