题解 IOI2016 railroad (Luogu P6168)
好题。(等于我基本上不会做的题)
题目大意:
有n个站台,每个站台有一个参数\((s,t)\)表示进入站台时火车速度不超过\(s\),出站台时火车速度为\(t\)。
可以用\(x\)的代价让火车速度减少\(x\),火车初始速度为\(1\)。
问最少的代价让所有站台恰好经过一次。
solution
将火车速度看做点,每一个车站\((s,t)\)建一条\(s->t\)的边,同时我们加上一个\(inf->1\)的车站,我们要求的就是一条代价最小的经过所有这些边的欧拉回路。
将“进入路段时速度\(≤si\)”转换为:“进入路段时速度恰好等于\(si\),并且铺设铁轨有加速和减速两种,加速无需代价,减速每\(1\)花费\(1\)的代价”。即\(x\)走向\(x+1\)不需要代价,而\(x+1\)走向\(x\)需要\(1\)的代价。
将车站离散化,之后然后考虑一段区间\([x,x+1]\),在欧拉回路中从左往右经过它的次数应该等于从右往左经过的次数。记\(bal\)为两者之差,则:
- 如果\(bal\)为负则可以直接加\(-bal\)条边(加速免费)
- 否则需要花\(bal\)的代价加\(bal\)条边
由于进行了离散化,不会存在不连通的点,于是我们直接跑一遍\(MST\)即可
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
#define N 500002
using namespace std;
const int inf=1<<30;
struct edge{
int u,v,w;
}e[N];
int n,m,op,i,l[N],r[N],a[N],cnt,d[N],fa[N],ans;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int my_comp(const edge &a,const edge &b){
return a.w<b.w;
}
signed main(){
n=read();op=read();
for(i=1;i<=n;i++){
l[i]=read();r[i]=read();
a[++cnt]=l[i];a[++cnt]=r[i];
}
a[++cnt]=inf;a[++cnt]=-inf;
sort(a+1,a+cnt+1);
cnt=unique(a+1,a+cnt+1)-a-1;
for(i=1;i<=n;i++){
l[i]=lower_bound(a+1,a+cnt+1,l[i])-a;
r[i]=lower_bound(a+1,a+cnt+1,r[i])-a;
}
for(i=1;i<=cnt;i++)fa[i]=i;
d[2]=-1;d[cnt]=1;
for(i=1;i<=n;i++){
d[l[i]+1]++,d[r[i]+1]--;
fa[find(l[i])]=find(r[i]);
}
for(i=1;i<=cnt;i++) d[i]+=d[i-1];
for(i=2;i<=cnt;i++){
if(d[i]==0) continue;
if(d[i]>0) ans+=d[i]*(a[i]-a[i-1]);
fa[find(i-1)]=find(i);
}
for(i=2;i<cnt-1;i++){
if(find(i)!=find(i+1)) e[++m]=(edge){i,i+1,a[i+1]-a[i]};
}
sort(e+1,e+m+1,my_comp);
for(i=1;i<=m;i++){
int f1=find(e[i].u),f2=find(e[i].v);
if(f1!=f2){
ans+=e[i].w;
fa[f1]=f2;
}
}
printf("%lld\n",ans);
return 0;
}