1 条题解

  • 0
    @ 2026-5-5 11:23:27

    Problem Link

    题目大意

    给定长度为 nn 的序列 a1ana_1\sim a_n,给定 kk 个位置不能交换,其他位置上的元素可以任意交换,最小化 i=1n1max(ai,ai+1)\sum\limits_{i=1}^{n-1}\max(a_i,a_{i+1})

    数据范围:n300,k6n\le 300,k\le 6

    思路分析

    k=0k=0 的情况开始,注意到 $\max(a_i,a_{i+1})=\dfrac 12(a_i+a_{i+1}+|a_i-a_{i+1}|)$,因此我们只要最小化 aiai+1\sum |a_i-a_{i+1}|,升序或降序排列即可。

    回到一般的问题,我们设 a0=an+1=a_0=a_{n+1}=\infty 且这两个位置不能交换,那么整个序列就被不能交换的点分成若干个区间 (li,ri)(l_i,r_i),我们只要把可以交换的元素填入这些区间即可。

    假设我们在区间 (li,ri)(l_i,r_i) 中填的元素是 SS,根据刚才的结论,显然 SS 升序排列或降序排列最优,如果 ali<aria_{l_i}<a_{r_i} 那么升序排列,否则降序排列。

    SS 中元素的最大最小值分别为 maxi,mini\max_i,\min_i,那么这一段区间的贡献就是 alimini+arimaxi+maximini|a_{l_i}-\min_i|+|a_{r_i}-\max_i|+\max_i-\min_i

    注意到关于 maxi\max_i 的贡献为 arimaxi+maxi|a_{r_i}-\max_i|+\max_i 随着 maxi\max_i 变小递减,关于 mini\min_i 的贡献 aliminimini|a_{l_i}-\min_i|-\min_i 随着 mini\min_i 变大递减。

    因此可以通过调整法证明每个区间内的值域区间 [mini,maxi][\min_i,\max_i] 要么相离要么包含,否则可以把相交区间切成两部分,使得 maxi\max_i 变小 minj\min_j 变大。

    那么就可以考虑在值域上 dp,设可以交换的元素排序后是 w1wmw_1\sim w_m,那么设 fl,r,Sf_{l,r,S} 表示考虑 SS 中的区间用 wlwrw_l\sim w_r 中的元素填满的最小代价。

    根据代价函数的性质进一步分析,我们发现:对于两个值域区间 [mini,maxi][minj,maxj][\min_i,\max_i]\subseteq[\min_j,\max_j][mini,maxi][\min_i,\max_i] 内部不可能有元素在 jj 中,否则交换该元素和 mini\min_i 即可让 mini\min_i 变大。

    SS 中的区间大小总和为 sizSsiz_S,那么我们只要在 rl+1=sizSr-l+1=siz_S 时考虑向 SS 中插入一个区间,否则直接从 min(fl,r1,S,fl+1,r,S)\min(f_{l,r-1,S},f_{l+1,r,S})

    考虑插入区间的形式:

    • 合并两个值域区间,枚举 TST\subset S 后从 f(l,l+sizT1,T)+f(l+sizT,r,ST)f(l,l+siz_T-1,T)+f(l+siz_T,r,S\setminus T) 转移。
    • 插入一个区间 xx
      • 特判 xx 大小为 11 的情况,可以钦定这种情况最先考虑,只需要在 l=rl=r 时计算,通过合并操作即可转移。
      • 否则一定插入 minx=wl,maxx=wr\min_x=w_l,\max_x=w_r 的若干个元素,算出其转移代价后从 f(l+1,r1,S{x})f(l+1,r-1,S\setminus\{x\}) 转移。

    时间复杂度 O(n2(3k+k2k))\mathcal O(n^2(3^k+k2^k))

    代码呈现

    #include<bits/stdc++.h>
    using namespace std;
    const int V=1e6+5;
    int a[305],L[10],R[10],len[10],sz[1<<7],w[305],id[10],f[305][305][1<<7];
    void chkmin(int &x,int y) { x=x<y?x:y; }
    signed main() {
    	int N,M,n=0,m=0,ans=0;
    	scanf("%d%d",&N,&M);
    	for(int i=1;i<=N;++i) scanf("%d",&a[i]),ans+=2*a[i];
    	for(int i=1;i<=M;++i) scanf("%d",&id[i]);
    	a[0]=a[N+1]=V,id[0]=0,id[M+1]=N+1,ans+=2*V;
    	for(int i=0;i<=M;++i) {
    		if(id[i+1]-id[i]==1) ans+=abs(a[id[i]]-a[id[i+1]]);
    		else {
    			L[m]=id[i],R[m]=id[i+1],len[m]=R[m]-L[m]-1;
    			for(int j=L[m]+1;j<=R[m]-1;++j) w[++n]=a[j];
    			if(a[L[m]]>a[R[m]]) swap(L[m],R[m]); ++m;
    		}
    	}
    	sort(w+1,w+n+1);
    	for(int s=0;s<(1<<m);++s) for(int i=0;i<m;++i) if(s>>i&1) sz[s]+=len[i];
    	memset(f,0x3f,sizeof(f));
    	for(int i=0;i<=n;++i) f[i+1][i][0]=0;
    	for(int d=1;d<=n;++d) for(int l=1,r=d;r<=n;++l,++r) for(int s=0;s<(1<<m);++s) if(sz[s]<=d) {
    		f[l][r][s]=min(f[l][r-1][s],f[l+1][r][s]);
    		for(int t=(s-1)&s;t;t=(t-1)&s) {
    			chkmin(f[l][r][s],f[l][l+sz[t]-1][t]+f[l+sz[t]][r][s^t]);
    		}
    		if(sz[s]<d) continue;
    		for(int j=0;j<m;++j) if(s>>j&1) {
    			int z=abs(a[L[j]]-w[l])+abs(a[R[j]]-w[r])+w[r]-w[l];
    			if(len[j]==1&&d==1) {
    				chkmin(f[l][r][s],z);
    			} else if(len[j]>1) {
    				chkmin(f[l][r][s],f[l+1][r-1][s^(1<<j)]+z);
    			}
    		}
    	}
    	printf("%d\n",(ans+f[1][n][(1<<m)-1])/2-2*V);
    	return 0;
    }
    
    • 1

    信息

    ID
    7611
    时间
    2000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    13
    已通过
    2
    上传者