专栏文章
CF1497E2题解
CF1497E2题解参与者 2已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @minqlml0
- 此快照首次捕获于
- 2025/12/02 06:44 3 个月前
- 此快照最后确认于
- 2025/12/02 06:44 3 个月前
题解没有 的 dp 做法,于是写一下。尽管预处理是 的。
首先注意到应该有一定的贪心性质,在分段数相同的时候,你希望最后一个分割点更靠后。
然后考虑完全平方数有什么性质,其因子的次数都是偶数。
两个数相乘为一个完全平方数,本质上和他们所含有的偶数次方因子没有关系。只需把这些除掉就转化成了两个数相等的问题。预处理一个小常数单根号是容易做的,可以进一步优化但没有必要。
然后考虑完全平方数有什么性质,其因子的次数都是偶数。
两个数相乘为一个完全平方数,本质上和他们所含有的偶数次方因子没有关系。只需把这些除掉就转化成了两个数相等的问题。预处理一个小常数单根号是容易做的,可以进一步优化但没有必要。
再考虑你转化后怎么做,相等的情况可以记一个 表示和 相同的上一个,格式化想到一个 表示前 个分 段的最少段数,发现是没有办法转移的,因为你不知道上一个分段点。
不妨用 把答案和转移点都记下来,每次从前转移。
不妨用 把答案和转移点都记下来,每次从前转移。
但是你其实并不需要决策转移点,因为考虑前 个和前 个的区别本质就是第 个位置造成的冲突。
记分段点为 ,即最后一段的开头。
如果 ,该冲突在这一段不存在,可以直接继承 。
否则冲突存在,处理方法是改掉一个或者新开一段。
也就是从 或者 转移。
记分段点为 ,即最后一段的开头。
如果 ,该冲突在这一段不存在,可以直接继承 。
否则冲突存在,处理方法是改掉一个或者新开一段。
也就是从 或者 转移。
这个转移为什么是对的,考虑你在计算 的时候前面的东西应该被算完了,并且你已经记住了它的最优决策点。
所以每个位置的最优决策点应该是唯一的,所以就省去了枚举决策点的一个 。
所以每个位置的最优决策点应该是唯一的,所以就省去了枚举决策点的一个 。
具体的方程可以看代码和注释,是简单的。主要想分享这种预先知道决策点复杂度省一个 的东西。
CPP#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]
*/
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...