[欧拉回路 最小生成树] UOJ#236 -- IOI2016. railroad

对每个速度建一个点, s t 连一条边,那么就是要再加一些边使得能走过所有边。

加一条 inf 1 的边,题目就转换为求最小代价使图变成欧拉图

考虑这样一个区间 [i,i+1] , 只有满足 si,ti+1 的边与满足 ti,si+1 的边的个数相同时图才可能是欧拉图。令 gi 等于第一种边的个数减去第二种边的个数

gi<0 时,那么这个点就相当于在车站中,可以不花费代价加一条 ii+1 的边

gi>0 时,那么就要花费1的代价加一条 i+1i 的边

欧拉图还有一个条件就是连通,做一遍最小生成树就可以了

#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;
}
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值