对每个速度建一个点,s 向
加一条 inf 到 1 的边,题目就转换为求最小代价使图变成欧拉图
考虑这样一个区间
当 gi<0 时,那么这个点就相当于在车站中,可以不花费代价加一条 i→i+1 的边
当 gi>0 时,那么就要花费1的代价加一条 i+1→i 的边
欧拉图还有一个条件就是连通,做一遍最小生成树就可以了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N=500010;
vector<int> ls;
int pre[N],fa[N];
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
struct edge{
int a,b,w;
friend bool operator <(edge a,edge b){
return a.w<b.w;
}
}e[N];
ll plan_roller_coaster(vector<int> s,vector<int> t){
int n=s.size();
for(int i : s) ls.push_back(i);
for(int i : t) ls.push_back(i);
ls.push_back(1); ls.push_back(1e9);
sort(ls.begin(),ls.end()); ls.resize(unique(ls.begin(),ls.end())-ls.begin());
for(int &i : s) i=lower_bound(ls.begin(),ls.end(),i)-ls.begin()+1;
for(int &i : t) i=lower_bound(ls.begin(),ls.end(),i)-ls.begin()+1;
int m=ls.size();
pre[2]=-1; pre[m+1]=1;
for(int i=1;i<=m;i++) fa[i]=i;
fa[find(1)]=find(m);
for(int i=0;i<n;i++){
pre[s[i]+1]++,pre[t[i]+1]--;
fa[find(s[i])]=find(t[i]);
}
for(int i=1;i<=m;i++) pre[i]+=pre[i-1];
ll ret=0;
for(int i=1;i<m;i++){
if(pre[i+1]==0) continue;
if(pre[i+1]>0) ret+=1LL*pre[i+1]*(ls[i]-ls[i-1]);
fa[find(i)]=find(i+1);
}
int cnt=0;
for(int i=1;i<m;i++)
if(find(i)!=find(i+1)) e[++cnt]={i,i+1,ls[i]-ls[i-1]};
sort(e+1,e+1+cnt);
for(int i=1;i<=cnt;i++){
if(find(e[i].a)==find(e[i].b)) continue;
fa[find(e[i].a)]=find(e[i].b);
ret+=e[i].w;
}
return ret;
}
/*
int main(){
vector<int> s,t;
s.push_back(1); s.push_back(4); s.push_back(5); s.push_back(6);
t.push_back(7); t.push_back(3); t.push_back(8); t.push_back(6);
cout<<plan_roller_coaster(s,t)<<endl;
}
*/