Solution -「Gym 102979L」 Lights On The Road
Link.
给定序列 ,选择 位置的代价为 ,要求每个位置要不被选择,要不左右两个位置至少被选择一个。求前 小的选择代价。
。
建图,边形如 ,再引入左右两个虚点 ,那么每种方案对应从 到 的一条路径,题目即求无负权图上的前 小路径,是道模板题,很可惜我并不会。(
还是用前 小的一贯套路:用堆维护,每次取当前最优方案扩展。首先在建出以 为根的内向最短路树,考虑用一条非树边 替换最短路 的一段,则有 ,且满足 ,其中 即 到 的最短路。这样我们得到了扩展一次的方法:取 到 最短路的邻接边 中, 最小的边替换最短路。
如果替换多条边呢?可以发现有两种替换方法:
- 将当前最优的 替换为新的 ;
- 在当前 的基础上,取 到 最短路的邻接边扩展。
那么用可持久化左偏树维护每个点到 的可用邻接边集合即可支持扩展。设图的点数为 ,边数为 ,整个复杂度则为 ,本题则有 。
/*~Rainybunny~*/
#include <bits/stdc++.h>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
typedef long long LL;
typedef std::pair<LL, int> PLI;
typedef std::pair<int, int> PII;
#define fi first
#define se second
const int MAXN = 2.5e5;
int n, m, pre[MAXN + 5], root[MAXN + 5];
LL dis[MAXN + 5];
std::vector<PII> oriG[MAXN + 5], revG[MAXN + 5];
struct PersistentLeftistTree {
static const int MAXND = 3e7;
int node, ch[MAXND + 5][2], rds[MAXND + 5];
PLI val[MAXND + 5]; // (cost, end point).
inline int newnd( const PLI& v ) {
val[++node] = v, ch[node][0] = ch[node][1] = 0, rds[node] = 1;
return node;
}
inline int merge( int x, int y ) {
if ( !x || !y ) return x | y;
if ( val[x] > val[y] ) std::swap( x, y );
int u = newnd( val[x] );
ch[u][0] = ch[x][0], ch[u][1] = merge( ch[x][1], y );
if ( rds[ch[u][1]] > rds[ch[u][0]] ) std::swap( ch[u][0], ch[u][1] );
return rds[u] = rds[ch[u][1]] + 1, u;
}
} plt;
inline void link( const int s, const int t, const int w ) {
oriG[s].push_back( { t, w } ), revG[t].push_back( { s, w } );
// std::cerr << s << ' ' << t << ' ' << w << '\n';
}
inline void dijkstra() {
static std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > heap;
memset( dis, 0x3f, sizeof dis );
heap.push( { dis[n + 1] = 0, n + 1 } );
while ( !heap.empty() ) {
PLI p( heap.top() ); heap.pop();
if ( dis[p.se] < p.fi ) continue;
for ( const PII& e: revG[p.se] ) {
if ( dis[e.fi] > p.fi + e.se ) {
heap.push( { dis[e.fi] = p.fi + e.se, e.fi } );
pre[e.fi] = p.se;
}
}
}
}
inline void solve() {
per ( u, n, 0 ) {
root[u] = root[pre[u]];
for ( const PII& v: oriG[u] ) {
if ( pre[u] != v.fi ) {
root[u] = plt.merge( root[u],
plt.newnd( { dis[v.fi] + v.se - dis[u], v.fi } ) );
}
}
}
static std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > heap;
std::cout << dis[0] << '\n';
if ( root[0] ) heap.push( { plt.val[root[0]].fi, root[0] } );
while ( --m ) {
if ( heap.empty() ) { std::cout << "-1\n"; continue; }
PLI p( heap.top() ); heap.pop();
std::cout << dis[0] + p.fi << '\n';
int u;
if ( ( u = plt.merge( plt.ch[p.se][0], plt.ch[p.se][1] ) ) ) {
heap.push( { p.fi - plt.val[p.se].fi + plt.val[u].fi, u } );
}
if ( ( u = root[plt.val[p.se].se] ) ) {
heap.push( { p.fi + plt.val[u].fi, u } );
}
}
}
int main() {
std::ios::sync_with_stdio( false ), std::cin.tie( 0 );
std::cin >> n >> m;
rep ( i, 1, n ) {
int w; std::cin >> w;
rep ( j, 1, 3 ) {
if ( i + j <= n || ( i + j == n + 1 && j < 3 ) ) {
link( i, i + j, w );
}
}
}
link( 0, 1, 0 );
if ( n > 1 ) link( 0, 2, 0 );
dijkstra();
// rep ( i, 0, n + 1 ) std::cout << dis[i] << ' ' << pre[i] << '\n';
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现