题意:
有n个正整数,你至多可以把其中k个修改为任意的正整数
你需要把这n个数划分为若干个连续的段,满足每一段的任意两个数的乘积都不是完全平方数
问最少划分为多少段
任意两个数的乘积都不是完全平方数
等价于
把区间内的数质因子分解,每个质因子的指数对2取模,任意两个数的结果都是不同的。
因为可以修改为任意的正整数,所以相当于每个区间内质因子分解指数对2取模的结果至多有k个位置可以与区间内前面的数相同
设dp[i][j]表示前i个数至多修改了j个位置的最少划分段数
枚举最后一段修改了p个位置,假设这最后一段是区间[x,i],那么dp[i][j]可以用dp[x-1][j-p]+1更新
就剩下x怎么求了
预处理le[i][j]表示区间[L,i]至多修改j个数,满足要求的最小的L
这个可以先枚举j,对于每个j用双指针的方法解决
#include<bits/stdc++.h> using namespace std; #define N 200004 int dp[N][21],le[N][21]; int cnt[10000001]; int a[N]; int main() { int T,n,k,x,y,l; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); x=1; for(int j=2;j*j<=a[i];++j) if(!(a[i]%j)) { y=0; while(!(a[i]%j)) { ++y; a[i]/=j; } if(y&1) x*=j; } a[i]*=x; } for(int i=0;i<=k;++i) { x=0; l=1; for(int r=1;r<=n;++r) { cnt[a[r]]++; if(cnt[a[r]]!=1) ++x; while(x>i) { cnt[a[l]]--; if(cnt[a[l]]) --x; ++l; } le[r][i]=l; } while(l<=n) cnt[a[l++]]--; } for(int i=1;i<=n;++i) for(int j=0;j<=k;++j) dp[i][j]=n; for(int i=1;i<=n;++i) for(int j=0;j<=k;++j) for(int p=0;p<=j;++p) dp[i][j]=min(dp[i][j],dp[le[i][p]-1][j-p]+1); printf("%d\n",dp[n][k]); } }