题解
第一次写博客,感觉好激动啊~
其实这题的Subtask 2很有启发意义
Subtask 2中,由于红点在蓝点左边,所以答案就是
回到原题,考虑把连接点的序列分成几段的红色,蓝色,…相间
这时应该把每一段切开,左边的连向前一段,右边的连向后一段
这时花费的代价就是相邻两段(蓝红或者红蓝)连起来所需的代价(参照Subtask 2的公式)
这个可以用dp解决,设 是当前第 个点所在段在位置 切开时,前 个点的最小代价
根据第 段和第 段的大小关系分类讨论,并维护相应的值,可以做到 转移
最终答案就是 (最后一个点必须切开),总时间复杂度为 。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=100005,inf=1ll<<60;
ll n1,n2,n,dp[N],f[N],g[N],sum[N],sz,sz1,st;
struct point{ll x,col;bool operator<(point a){return x<a.x;}}p1[N],p[N];
long long min_total_length(vector<int> red, vector<int> blue) {
n1=red.size(),n2=blue.size();
for(ll i=0;i<n1;i++)p1[n++]=(point){red[i],1};
for(ll i=0;i<n2;i++)p1[n++]=(point){blue[i],2};
merge(p1,p1+n1,p1+n1,p1+n,p+1);
for(ll i=1;i<=n;i++){
if(p[i].col!=p[i-1].col){
for(int j=i;p[j].col==p[i].col;j++)sum[i]+=p[j].x;
for(int j=i-1;j>=st;j--){
f[j]=p[i-1].x*(i-j)-sum[j]+min(dp[j],dp[j-1]);
if(i!=j+1)f[j]=min(f[j],f[j+1]);
}
for(int j=st;j<i;j++){
g[j]=p[i].x*(i-j)-sum[j]+min(dp[j],dp[j-1]);
if(j!=st)g[j]=min(g[j],g[j-1]);
}
st=i,sz1=sz,sz=1;
}else ++sz,sum[i]=sum[i-1]-p[i-1].x;
if(st==1){dp[i]=inf;continue;}
ll cursum=sum[st]-sum[i]+p[i].x;
if(sz1<=sz)dp[i]=f[st-sz1]-sz*p[st-1].x+cursum;
else dp[i]=min(g[st-sz-1]-sz*p[st].x+cursum,f[st-sz]-sz*p[st-1].x+cursum);
}
return dp[n];
}