2024年11月上海月赛丙组题解
参赛经历
在模意义下我们有
于是我们枚举所有最后答案可能的形态,枚举前两个数字,那么第三个数和之后的序列显然唯一确定。
再考虑如何将初始序列变为目标序列,修改操作对于每个位置是独立的,那么我们每个位置不断加一直到等于目标序列为止。最后在所有答案可能的情况中取最小值。
这样总时间复杂度
示例实现:
#include <bits/stdc++.h>
using namespace std;
int f(int l,int r)
{
if(l<=r)
return r-l;
return r+3-l;
}//计算一个位置转移到目标位置的代价
const int maxn=2e6+10;
int a[maxn],a0[maxn],a1[maxn],a012[maxn],a021[maxn],n,a2[maxn];
int comp_012(int del)
{
int ans=0;
for(int i=0;i<=n-1;i++)
ans+=f(a[1+i],a012[1+i+del]);
return ans;
}//计算总代价
int comp_021(int del)
{
int ans=0;
for(int i=0;i<=n-1;i++)
ans+=f(a[1+i],a021[1+i+del]);
return ans;
}//同上
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],a[i]%=3;
int ans=3*n;//答案上界显然是2n
int minn0=0;
//为了便于编写,分类讨论简化为000,111,222,012,021五种情况,类似120这些情况用位移的方法消除了,实际上可以枚举前两个数之后统一构造数列
for(int i=1;i<=n;i++)
a0[i]=0,minn0+=f(a[i],a0[i]);//处理全0序列
ans=min(ans,minn0);
minn0=0;
for(int i=1;i<=n;i++)
a1[i]=1,minn0+=f(a[i],a1[i]);//处理全1序列
ans=min(ans,minn0);
minn0=0;
for(int i=1;i<=n;i++)
a2[i]=2,minn0+=f(a[i],a2[i]);//全2序列
ans=min(ans,minn0);
for(int i=1;i<=3*n;i+=3)
a012[i]=0,a012[i+1]=1,a012[i+2]=2;//形如012的序列
for(int i=0;i<10;i++)
ans=min(ans,comp_012(i));//处理循环位移
for(int i=1;i<=3*n;i+=3)
a021[i]=0,a021[i+1]=2,a021[i+2]=1;
for(int i=0;i<10;i++)
ans=min(ans,comp_021(i));//同上
cout<<ans<<'\n';
}