题解 CF612F 【Simba on the Circle】

· · 题解

挺烦人的一道DP题。

首先,本题的阶段是很容易设想出来的:因为要求“输出的东西不降”,所以必须先将所有 1 输出,然后是所有 2,然后是所有 3……在不同的数字间,转移是有序的;但是在同一数字间,转移就可能不太trival了。

首先先设出DP状态。设 f_i 表示若 i 是所有值为 a_i 的位置中第一个被输出的,此时所移动的最短距离,再设 g_i 表示若 i 是所有值为 a_i 的位置中最后一个被输出的,此时所移动的最短距离。则答案即为

\min\limits_{a_i\text{ is maximum}}g_i

先考虑不同数字间的转移。假如 j,k 分别是一个小一点的数和一个大一点的数,则有

g_j+\min\Big\{(k-j)\bmod n,(j-k)\bmod n\Big\}\rightarrow f_k

因为 n 只有 2000,所以暴力枚举这样的 (j,k) 进行转移即可。

然后再考虑相同数字间的转移。我们可以大胆猜测,假如我们从一个 f_j 转移到 g_k,则必定是先顺时针/逆时针走到 k 的前一个数,然后再掉头一路走到 k

具体式子很长,直接看代码即可。明显这里仍然可以 n^2 枚举转移。

比较恶心的是输出,不同数字间的转移还好,烦人的是相同数字间的转移。按照上文所述的实际意义进行转移即可,详情见代码。

总时间复杂度 O(n^2)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,p,a[2010],f[2010],g[2010],F[2010],G[2010],res=0x3f3f3f3f;//f:the minimal starting from i; g:the minimal ending at i
vector<int>v[2010],u;
stack<int>s;
int main(){
    scanf("%d%d",&n,&p),p--,memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g));
    for(int i=0;i<n;i++)scanf("%d",&a[i]),u.push_back(a[i]);
    sort(u.begin(),u.end()),u.resize(m=unique(u.begin(),u.end())-u.begin());
    for(int i=0;i<n;i++)v[a[i]=lower_bound(u.begin(),u.end(),a[i])-u.begin()+1].push_back(i);
//  for(int i=0;i<n;i++)printf("%d ",a[i]);puts("");
//  for(int i=1;i<=m;i++){for(auto j:v[i])printf("%d ",j);puts("");}
    for(auto i:v[1])f[i]=min((i+n-p)%n,(p+n-i)%n),F[i]=-1;
    for(int i=1;i<=m;i++){
        int len=v[i].size();
        for(int j=0;j<len;j++)for(int k=0;k<len;k++){
            if(j==k)continue;
            int J=v[i][j],K=v[i][k];
            int A=(v[i][(k+len-1)%len]+n-J)%n*2+(J+n-K)%n;
            int B=(J+n-v[i][(k+1)%len])%n*2+(K+n-J)%n;
            if(g[K]>f[J]+min(A,B))g[K]=f[J]+min(A,B),G[K]=J;
        }
        if(len==1)g[v[i][0]]=f[v[i][0]],G[v[i][0]]=v[i][0];
        if(i==m)continue;
        for(auto j:v[i])for(auto k:v[i+1])if(f[k]>g[j]+min((k+n-j)%n,(j+n-k)%n))f[k]=g[j]+min((k+n-j)%n,(j+n-k)%n),F[k]=j;
    }
//  for(int i=0;i<n;i++)printf("%d ",f[i]);puts("");
//  for(int i=0;i<n;i++)printf("%d ",g[i]);puts("");
    for(auto i:v[m])res=min(res,g[i]);
    printf("%d\n",res);
    for(auto K:v[m]){
        if(g[K]!=res)continue;
        for(int i=m;i;i--){
            int len=v[i].size();
            int J=G[K];
            int j=lower_bound(v[i].begin(),v[i].end(),J)-v[i].begin(),k=lower_bound(v[i].begin(),v[i].end(),K)-v[i].begin();
            int A=(v[i][(k+len-1)%len]+n-J)%n*2+(J+n-K)%n;
            int B=(J+n-v[i][(k+1)%len])%n*2+(K+n-J)%n;
            if(len>1){
                if(A<B){
                    for(int l=k;(l+1)%len!=j;(++l)%=len)s.push(-(v[i][(l+1)%len]+n-v[i][l])%n);
                    s.push(-(v[i][(k+len-1)%len]+n-v[i][(j+len-1)%len])%n);
                    for(int l=(k+len-1)%len;l!=j;(l+=len-1)%=len)s.push((v[i][l]+n-v[i][(l+len-1)%len])%n);
                }else{
                    for(int l=k;(l+len-1)%len!=j;(l+=len-1)%=len)s.push((v[i][l]+n-v[i][(l+len-1)%len])%n);
                    s.push((v[i][(j+1)%len]+n-v[i][(k+1)%len])%n);
                    for(int l=(k+1)%len;l!=j;(++l)%=len)s.push(-(v[i][(l+1)%len]+n-v[i][l])%n);
                }               
            }
            A=J,B=F[J];
            if(B==-1)B=p;
            s.push((A+n-B)%n<(B+n-A)%n?(A+n-B)%n:-(B+n-A)%n);
            K=B;
        }
        break;
    }
//  auto S=s;while(!S.empty())printf("%d ",(p+=S.top()+n)%n),S.pop();puts("");
    while(!s.empty())printf("%c%d\n",(s.top()>=0?'+':'-'),abs(s.top())),s.pop();
    return 0;
}