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)\) 的复杂度通过本题。
后记
- 在本题中,尽量不要使用 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;
}