CF1497E2题解
Forgive_Me · · 题解
题解没有
首先注意到应该有一定的贪心性质,在分段数相同的时候,你希望最后一个分割点更靠后。
然后考虑完全平方数有什么性质,其因子的次数都是偶数。
两个数相乘为一个完全平方数,本质上和他们所含有的偶数次方因子没有关系。只需把这些除掉就转化成了两个数相等的问题。预处理一个小常数单根号是容易做的,可以进一步优化但没有必要。
再考虑你转化后怎么做,相等的情况可以记一个
不妨用
但是你其实并不需要决策转移点,因为考虑前
记分段点为
如果
否则冲突存在,处理方法是改掉一个或者新开一段。
也就是从
这个转移为什么是对的,考虑你在计算
所以每个位置的最优决策点应该是唯一的,所以就省去了枚举决策点的一个
具体的方程可以看代码和注释,是简单的。主要想分享这种预先知道决策点复杂度省一个
submission
#include<bits/stdc++.h>
using namespace std;
int a[200010],f[200010],dp[200010][21][2];
unordered_map<int,int> mp,to;
void solve(){
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int now=a[i];
for(int j=2;j*j<=now;j++){
while(now%(j*j)==0) now/=(j*j);
}
a[i]=now;
}
mp.clear();
for(int i=1;i<=n;i++){
if(mp[a[i]]) to[i]=mp[a[i]];
else to[i]=0;
mp[a[i]]=i;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=k;j++) dp[i][j][1]=1e9,dp[i][j][0]=0;
dp[1][0][0]=1,dp[1][0][1]=1;
dp[1][1][0]=1,dp[1][1][1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=min(i,k);j++){
int x=to[i];
if(dp[i-1][j][0]>x) dp[i][j][0]=dp[i-1][j][0],dp[i][j][1]=dp[i-1][j][1];
else
{
if(dp[i-1][j][1]+1<=dp[i][j][1]){
dp[i][j][0]=i;
dp[i][j][1]=dp[i-1][j][1]+1;
}
if(j&&(dp[i-1][j-1][1]<dp[i][j][1]||(dp[i-1][j-1][1]==dp[i][j][1]&&dp[i-1][j-1][0]>dp[i][j][0]))){
dp[i][j][0]=dp[i-1][j-1][0];
dp[i][j][1]=dp[i-1][j-1][1];
}
}
}
}
int ans=1e9;
for(int i=0;i<=k;i++){
ans=min(ans,dp[n][i][1]);
}
cout<<ans<<'\n';
return ;
}
signed main(){
//freopen("seq.in","r",stdin);
//freopen("seq.out","w",stdout);
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;while(T--) solve();
}
/*
尽量往后划
f[i][j][0/1]=f[i-1][j][0/1]
to[i]
if(f[i-1][j][0]>to[i]) f[i][j][0/1]=f[i-1][j][0/1]
else
f[i][j][0]=i,f[i][j][1]=f[i-1][j]+1
f[i][j][0]=f[i-1][j-1][0],f[i][j][1]=f[i-1][j-1][1]
*/