题面:
解法:
首先特判全1的情况,
此时答案一定是n-k.
对于非全1的情况, 一次有效操作一定能获得1的收益
(有效操作指的是能让gcd=1的相邻pair变成gcd!=1的pair)
我们考虑什么情况下才能让一次操作获得额外的收益:
1. 对于非1且gcd(a[i],a[i+1])=1的连续子数组:
手玩一下容易发现一定是间隔着操作最优,
例如3 5 7 9 11变为3 0 7 0 12最优,
其中的每次操作都能获得2的收益,即其中有两次操作可有获得额外的1收益.
对于这样长度为len的区间, 能获得额外收益的次数为(len-1)/2次.
2. 对于全1的子区间:
如果该区间在数组的头部或者尾部, 那么一定无法获得额外收益.
如果该区间在数组的中间部分, 那么当子区间全部被消为0时,才能获得额外的1收益.
如果子区间没有全部消为0,那么每次操作都没有额外的收益.
例如2 1 1 1 2, 操作3次后变为2 0 0 0 2, 收益为4.
因此我们有这样的一个操作策略:
先贪心的进行可以获得额外收益的操作.
(对于上述的情况2,显然先操作区间长度小的)
当无法获得额外收益时,则只进行普通的有效操作.
代码实现我是参考jiangly的代码修改的,算是比较容易看懂的写法了。
Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PI pair<int, int>
const int maxm = 2e5 + 5;
int n,k;
int a[maxm];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void solve() {
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int c1=0;
for(int i=1;i<=n;i++){
c1+=a[i]==1;
}
if (c1==n){
cout<<n-k<<endl;
return;
}
int ans=0;
for(int i=2;i<=n;i++){
if(gcd(a[i],a[i-1])==1){
ans++;
}
}
vector<int> e;
for(int l=1;l<=n;l++){
if(a[l]==1)continue;
int r=l;
while(r+1<=n && a[r+1]!=1 && gcd(a[r],a[r+1])==1){
r++;
}
int cnt=(r-l)/2;
while(cnt--)e.push_back(1);
l=r;
}
for(int l=1;l<=n;l++){
if(a[l]!=1)continue;
int r=l;
while(r+1<=n && a[r+1]==1){
r++;
}
if(l!=1 && r!=n){
e.push_back(r-l+1);
}
l=r;
}
sort(e.begin(),e.end());
for(auto x:e){
if(k>=x){
k-=x;
ans-=x;
ans--;
}
}
ans-=min(ans,k);
cout<<ans<<endl;
}
signed main() {
#define MULTI_CASE
ios::sync_with_stdio(0);
cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("../in.txt", "r", stdin);
freopen("../out.txt", "w", stdout);
#endif
#ifdef MULTI_CASE
int T;
cin >> T;
while (T--)
#endif
solve();
return 0;
}
官方题解: