Solution - CF1497E2
\(\color{9D3DCF}\text{CF1497E2 Square-free division (hard version)}\)
\(\color{000000}\text{1497 E2. Square-Free Division (hard version)}\)
首先发现题目中要求使得两个数之积都不是完全平方数,那么我们可以先预处理 \(a_i\),将 \(a_i\) 中的构成平方的因子先除掉(即将 \(a_i\) 中次数大于 \(1\) 的质因子的次数 \(\bmod\ 2\)),显然去除每个数的平方因子后,两个数相乘为完全平方数当且仅当它们相等。
看到 \(n\leq 2\times 10^5,k\leq 20\),自然想到 \(O(nk)\) 或 \(O(nk^2)\) 的 DP。
设 \(f_{i,j}\) 表示前 \(i\) 个数中修改 \(j\) 个划分的最小段数,\(g(i,j)\) 表示表示以 \(i\) 为终点,在 \(i\) 所在的划分段内修改了 \(j\) 次往回最远能扩充到的点。
则有
时间复杂度 \(O(nk^2)\)。
那么如何求 \(g\) 呢?
我们发现在 \(g(i,j)\) 中,对于一个确定的 \(j\),当 \(i\) 增大时,\(g(i,j)\) 必然单调不减,所以对于一个 \(j\),我们能够用双指针 \(O(n)\) 求出。
具体实现为开一个桶记录 \(a_i\) 出现的次数,用双指针维护一个满足修改次数小于等于 \(j\) 的区间,即可求出 \(g(i,j)\),时间复杂度 \(O(nk)\)。
总时间复杂度是 \(O(nk^2)\),可以通过本题。
后记:
在本题中,尽量不要使用 memset 函数给数组进行赋初值(都是血泪教训)。
memset 函数的时间复杂度为 \(O(n)\),其中 \(n\) 为数组的字节大小,具体可以看这篇文章。
本题可以通过构造数据使得 memset 函数 TLE。
#include<cstdio>
#include<cstring>
const int N=2e5+10,K=25,S=1e7+10;
const int inf=0x3f3f3f3f;
int T,n,k;
int ans;
int a[N];
int num[S];
int f[N][K],g[N][K];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9')ch=='-'?f=0:0,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?x:-x;
}
inline int min(int a,int b){return a<b?a:b;}
int main(){
T=read();
while(T--){
n=read(),k=read(),ans=inf;
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]=inf;
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;
}
浙公网安备 33010602011771号