[USACO24FEB] Minimum Sum of Maximums P 题解

[USACO24FEB] Minimum Sum of Maximums P
第一步是想到转化一下这个 \(\sum \max(a_i,a_{i+1})\),我们发现 \(\max(a_i,a_{i+1}) = \frac{1}{2} \times (a_i+a_{i+1}+|a_i-a_{i+1}|)\)。那么答案前半部分就是固定的了,我们只要最小化 \(\sum |a_i-a_{i+1}|\),因为转移会用到 \(a_0\) 和 \(a_{n+1}\),所以给这两个位置赋极大值即可。

\(k\) 个固定点会将序列分成至多 \(k+1\) 段,假设我们已经知道了每个段内分配了那些数,我们来考虑怎么排是最优的。发现是从小到大排最优,这段的贡献就是 \(|V_l-mi|+|V_r-mx|+mx-mi\),那这样我们就只关心最大值和最小值了。

那么怎么列转移方程呢?需要发现一个关键性质,即每一个段的 \((mi,mx)\) 只会不相交或者包含,而不会有交,可以通过调整法证明。

我们把能用的 \(a_i\) 排序,我们会优先考虑填长度小的段,那就是区间 DP,而且看到 \(k \le 6\) 就想到状压,设 \(f_{l,r,s}\) 表示用排序后的数组的 \([l,r]\) 内的数能够填满段集合 \(s\) 的最小答案。我们要预处理出每个段的长度 \(wd_i\),以及每个状态掩码的段长之和 \(sum_s\)。

考虑转移,有几种情况。第一是把用自己区间范围的两端填好一个段,这个段的 \((mi,mx)\) 是包含了更小的段。这种转移条件是 \(len=sum_s\),就能保证是紧凑地填好的,否则区间范围如果更大的话,\(mx\) 变大,\(mi\) 变小就不优了。特判一下段长为 \(1\) 的情况。\(f_{l,r,s} \leftarrow f_{l+1,r-1,s-x}+|L_x-p_l|+|R_x-p_r|+p_r-p_l\)。

第二种情况是跳过,即 \(f_{l,r,s}=\min(f_{l+1,r,s},f_{l,r-1,s})\),在任何情况下都可以转移,表示留给下一个包住它的段。

第三种情况是合并不相交的段,考虑枚举子集,则 \(f_{l,r,s} \leftarrow f_{l,l+sum_{t}-1,t}+f_{l+sum_{t},r,s-t}\),其中 \(t \subseteq s\)。

现在我们做到了 \(O(n^23^k)\) 的时间复杂度,可以通过此题。但是我们可以发现第三种情况可以只在 \(len=sum_s\) 的时候转移,因为若 \(len >sum_s\) 则在之前肯定已经转移过了。那这样就是 \(O(n^2+n3^k)\)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,k,tot,m;
const int N = 3e2+10,V=(1<<7);
ll inf=1e14;
int a[N],b[N],ans=0,vis[N],wd[N];
ll L[N],R[N],sum[V];
ll f[N][N][V];
vector<int>p;
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        ans+=2*a[i];
    }
    a[0]=1e6,a[n+1]=1e6;ans+=2e6;
    for(int i=1;i<=k;i++){
        cin>>b[i];
        vis[b[i]]=1;
    }
    b[0]=0,b[k+1]=n+1;
    for(int i=1;i<=n;i++){
        if(!vis[i])p.push_back(a[i]);
    }
    p.push_back(-1);
    sort(p.begin(),p.end());
    m=p.size()-1;
    for(int i=0;i<=k;i++){
        if(b[i]==b[i+1]-1){
            ans+=abs(a[b[i+1]]-a[b[i]]);
            continue;
        }
        wd[tot]=b[i+1]-b[i]-1;
        L[tot]=a[b[i]],R[tot]=a[b[i+1]];
        if(L[tot]>R[tot])swap(L[tot],R[tot]);
        ++tot;
    }
    for(int i=0;i<(1<<tot);i++){
        for(int j=0;j<tot;j++){
            if(i&(1<<j)){
                sum[i]+=wd[j];
            }
        }
    }
    for(int l=0;l<=m+1;l++){
        for(int r=0;r<=m+1;r++){
            for(int mk=0;mk<(1<<tot);mk++){
                f[l][r][mk]=(mk==0?0:inf);
            }
        }
    }
    for(int len=1;len<=m;len++){
        for(int l=1;l+len-1<=m;l++){
            int r=l+len-1;
            for(int mk=0;mk<(1<<tot);mk++){
                f[l][r][mk]=min(f[l+1][r][mk],f[l][r-1][mk]);
                if(sum[mk]>len)continue;
                if(sum[mk]==len){
                    for(int j=0;j<tot;j++){
                        if(mk&(1<<j)){
                            if(wd[j]==1&&len==1){
                                f[l][r][mk]=min(f[l][r][mk],abs(L[j]-p[l])+abs(R[j]-p[r]));
                            }else if(wd[j]>1){
                                f[l][r][mk]=min(f[l][r][mk],f[l+1][r-1][mk^(1<<j)]+abs(L[j]-p[l])+abs(R[j]-p[r])+p[r]-p[l]);
                            }
                        }
                    }
                    for(int s=(mk-1)&mk;s;s=(s-1)&mk){
                        f[l][r][mk]=min(f[l][r][mk],f[l][l+sum[s]-1][s]+f[l+sum[s]][r][mk^s]);
                    }
                }
            }
        }
    }
    ans+=f[1][m][(1<<tot)-1];
    ans/=2;ans-=2e6;
    cout<<ans;
    return 0;
}
posted @ 2026-08-17 17:09  farenara_sayowell  阅读(17)  评论(0)    收藏  举报