CF1497E2 题解

CF1497E2题解

题面

原题传送门

思路

看到 \(n\leqslant2\times10^5,k\leqslant20\),自然想到 \(O(nk)\)\(O(nk^2)\) 的 DP。

\(f[i][j]\) 表示前 \(i\) 个数中修改 \(j\) 个划分的最小段数,\(g[i][j]\) 表示表示以 \(i\) 为终点,在 \(i\) 所在段内修改了 \(j\) 次向左最远能扩充到的点。

则有 \(f[i][j]=\min(f[g[i][x]-1,j-x]+1)\),那么如何求 \(g\) 呢?

我们发现在 \(g[i][j]\) 中,对于一个确定的 \(j\),当 \(i\) 增加时,\(g[i][j]\) 必然单调不减,所以对于一个 \(j\) 能双指针 \(O(n)\) 求,所以预处理 \(g\) 的复杂度是 \(O(nk)\)

DP 的复杂度是 \(O(nk^2)\),故我们使用 \(O(nk^2)\) 的复杂度通过本题。

后记

  1. 在本题中,尽量不要使用 memset 函数给数组进行赋值。memset 函数的时间复杂度为 \(O(n)\),其中 \(n\) 为数组的字节大小。本题可以通过构造数据使得 memset 函数 TLE。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=2e5+10,K=25,S=1e7+10,INNF=0x3f3f3f3f;
int T,n,k,ans,a[N],num[S],f[N][K],g[N][K];
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
} 
int main(){
	T=read();
	while(T--){
		n=read();k=read();ans=INNF;
		for(int i=1; i<=n; i++){
			a[i]=read();
			for(int j=2; j*j<=a[i]; j++) 
				while(a[i]%(j*j)==0)
					a[i]/=j*j;
		}
		for(int i=0; i<=k; i++){
			for(int j=1; j<=n; j++) num[a[j]]=0;
			for(int l=1,r=1,t=0; r<=n; r++){
				num[a[r]]++;
				t+=(num[a[r]]>1);
				while(t>i&&l<r){
					t-=(num[a[l]]>1);
					num[a[l]]--;
					l++;
				}
				g[r][i]=l;
			}
		}
		for(int i=1; i<=n; i++)
			for(int j=0; j<=k; j++){
				f[i][j]=INNF;
				for(int x=0; x<=j; x++)
					f[i][j]=min(f[i][j],f[g[i][x]-1][j-x]+1);
		}
		for(int i=0; i<=k; i++) ans=min(ans,f[n][i]);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2025-01-29 15:41  naroto2022  阅读(5)  评论(0)    收藏  举报