【图论】【建模】IOI2016 railroad
【图论】【建模】IOI2016 railroad
题目描述
Anna 在一个游乐园工作。她负责建造一个新的过山车铁路。她已经设计了影响过山车速度的
对于
-
当进入这个路段时,有一个速度限制:过山车的速度必须小于或等于
(每小时千米)。 -
当离开这个路段时,过山车的速度刚好是
,不管过山车进入该路段时的速度如何。
最后完成的过山车设计是一个以某种顺序包含这
两个特殊路段之间的每
最后的设计还必须满足以下要求:
-
过山车在进入这些特殊路段时不能违反任一个速度限制。
-
过山车的速度在任意时刻为正。
你的任务是找出这些路段之间铁轨的最小可能总长度(这些路段之间铁轨总长度的最小值)。
算法描述
嘿嘿,这是个图论题
翻译题面:有
解释下为什么可以看作
-
: 由于从 出去时值一定是 ,所以最短一定最优,将速度(原题面)刚好降到 。 -
: 不用降速,耗费为0,加速耗费也为0,可以看作加到了
如果将一个数带进这个排列模拟其变化(即原题的坐过山车),我们发现,对于每一个
所以我们将速度离散化。试图将它转化成边的问题,我们对于每一条轨道,将
首先将
(记得连边时并查集上合并两端点)
这时检查答案,发现我们事实上将这个图连成了若干子欧拉回路,要合并这些回路,就要在它们之间建一条双向边,其中提速边为0,降速边为
不愧是IOI题目
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5,inf = 1e9 + 7;
struct Edge{
int u,v;long long w;
}e[N * 4];
long long n,m,s[N],t[N],v[N * 4],sum[N * 4],cnt = 0,tot = 0;
long long ans = 0;
inline int sch(long long x)
{
int l = 1,r = cnt;
while(l < r)
{
int mid = (l + r) >> 1;
if(x <= v[mid]) r = mid;
else l = mid + 1;
}
return l;
}
struct UFS{
int fa[N * 4];
inline void init()
{
for(int i = 1;i <= cnt;i++) fa[i] = i;
}
inline int find(int x)
{
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
inline void unionn(int x,int y)
{
x = find(x);y = find(y);
fa[x] = y;
}
}p;
inline void add(int x,int y,int z)
{
++tot;
e[tot].u = x;
e[tot].v = y;
e[tot].w = z;
}
inline bool cmp(Edge x,Edge y)
{
return x.w < y.w;
}
signed main()
{
cin>>n>>m;
for(int i = 1;i <= n;i++)
{
cin>>s[i]>>t[i];
v[++cnt] = s[i]; v[++cnt] = t[i];
}
s[++n] = inf; t[++n] = 1; v[++cnt] = s[n];v[++cnt] = t[n];
sort(v + 1,v + cnt + 1);
cnt = unique(v + 1,v + cnt + 1) - (v + 1);
p.init();
for(int i = 1;i <= n;i++)
{
s[i] = sch(s[i]);
t[i] = sch(t[i]);
++sum[s[i]];--sum[t[i]];
p.unionn(s[i],t[i]);
}
for(int i = 1;i <= cnt - 1;i++)
{
sum[i] += sum[i - 1];
if(sum[i] > 0) ans += sum[i] * (v[i + 1] - v[i]);
if(sum[i]) p.unionn(i + 1,i);//i -> i + 1 or i + 1 -> i都要连边、连通块相同
}
for(int i = 1;i <= cnt - 1;i++)
if(p.find(i) != p.find(i + 1))
add(p.find(i),p.find(i + 1),v[i + 1] - v[i]);
sort(e + 1,e + tot + 1,cmp);
for(int i = 1;i <= tot;i++)
{
if(p.find(e[i].u) == p.find(e[i].v)) continue;
p.unionn(e[i].u,e[i].v);
ans += e[i].w;
}
cout<<ans;
return 0;
}
代码小细节:在这一段当中
for(int i = 1;i <= cnt - 1;i++)
{
sum[i] += sum[i - 1];
if(sum[i] > 0) ans += sum[i] * (v[i + 1] - v[i]);
if(sum[i]) p.unionn(i + 1,i);//i -> i + 1 or i + 1 -> i都要连边、连通块相同
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话