题目大意
和E1大意相同,只不过现在可以将k个数字改成任意数字
题解
我们可以从右往左进行查找,用left[i][j]记录从i开始改j个数字可以让这一段数字不同的最远的l,也就是记录最长区间l到i,这样我们可以对数组从左往右进行dp,dp的转移公式
dp[i][j]=min(dp[i][j-1],dp[left[i][lst]][j-lst]+1);
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX=2e5+5;
const int MAXN=1e7;
const int INF=1e9;
int a[MAX];
int prime[MAXN];
int left[MAX][25];
int cnt[MAXN];
int dp[MAX][25];
int init(){
for(int i=2;i<MAXN;i++){
if(!prime[i]){
for(int j=i;j<MAXN;j+=i){
prime[j]=i;
}
}
}
}
int main(){
int t;
init();
scanf("%d",&t);
while(t--){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
int x,last=0,pn=0;
scanf("%d",&x);
a[i]=1;
while(x>1){
if(last==prime[x]){
pn++;
}else{
a[i]*=pn%2?last:1;
last=prime[x];
pn=1;
}
x/=prime[x];
}
a[i]*=pn%2?last:1;
}
for(int j=0;j<=k;j++){
int l=n,now=0;
for(int i=n;i>0;i--){
while(l>0&&now+(cnt[a[l]]>0)<=j){
now+=(cnt[a[l]]>0);
cnt[a[l]]++;
l--;
}
if(cnt[a[i]]>1){
now--;
}
left[i][j]=l;
cnt[a[i]]--;
}
}
for(int i=0;i<=k;i++){
dp[0][i]=0;
}
for(int i=1;i<=n;i++){
dp[i][0]=INF;
}
for(int j=0;j<=k;j++){
for(int i=1;i<=n;i++){
if(j>0){
dp[i][j]=dp[i][j-1];
}
for(int lst=0;lst<=j;lst++){
dp[i][j]=min(dp[i][j],dp[left[i][lst]][j-lst]+1);
}
}
}
int ans=INF;
for(int i=0;i<=k;i++){
ans=min(ans,dp[n][i]);
}
printf("%d\n",ans);
}
}