CF1497E2 题解

CF1497E2题解

题面

原题传送门

思路

看到 n2×105,k20,自然想到 O(nk)O(nk2) 的 DP。

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

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

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

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

后记

  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 @   naroto2022  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
花开如火,也如寂寞。