原题链接:AT2434 JOI 公園 (JOI Park)

写完之后看到题解区的三分吓了一跳

分析与解答

由于最终答案与边权有关,所以不妨考虑判断一条边是否会对答案有贡献。

记 \(dis\) 表示以点 \(1\) 为源点的最短路径长度,那么答案可以表示为 \(X \times C + \sum\limits_{i=1}^{m} d_i\ [\max(dis_{a_i}, dis_{b_i}) \gt X]\),也就是说只有当一条道路两端点的最短路长度均大于当前 \(X\) 时,这条道路的才会对答案有贡献。

这一点是可以根据题目描述得到的。

然后注意到一个事实:虽然题目中说 \(X\) 可以取任意自然数,但是显然 \(X\) 取以点 \(1\) 为源点的一条最短路径长度时最优。显然易证·也就是说枚举 \(X\) 的值时只需要枚举 \(dis\) 数组即可。

所以我们只需要求出以点 \(1\) 为源点的最短路径长度,然后求出每一条边想要被计入答案所需要的最小 \(X\) 值,记为 \(val\),即 \(\max(dis_{a_i}, dis_{b_i})\),然后按照该值对边排序,最后按照枚举当前 \(val\) 计算答案即可。

形象地说,就是在对边进行排序只后,选用一条边 \(i\) 作为“分界点”,所有 \(val_j \gt val_i\) 的边 \(j\) 的边权都会计入答案,其他则不会。

记得开 long long

这里运用堆优化的 Dijkstra 算法求最短路,时间复杂度 \(\Theta(n \log n)\)

Code

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue> using namespace std; typedef long long ll;
typedef pair<long long,int> pli;
const int MAXN = 100010;
const int MAXM = 200010;
const ll INF = 0x3f3f3f3f3f3f3f3f; int n, m, c; struct edge1{
int u, v, w;
ll val; int ind;
bool operator<(const edge1 &o)const{return val < o.val;}
}a[MAXM];
struct edge{
int ne, to, w;
edge(int N=0,int T=0,int W=0):ne(N),to(T),w(W){}
}e[MAXM<<1];
int fir[MAXN], num = 0;
inline void join(int a, int b, int c)
{
e[++num] = edge(fir[a], b, c);
fir[a] = num;
} ll dis[MAXN];
bool vis[MAXN];
priority_queue<pli,vector<pli>,greater<pli> > h; inline void dijkstra(int s = 1)
{
for(int i=1;i<=n;i++)
dis[i] = INF, vis[i] = 0;
dis[s] = 0;
h.push(make_pair(dis[s], s));
while(!h.empty())
{
int u = h.top().second;
h.pop();
vis[u] = 1;
for(int i=fir[u];i;i=e[i].ne)
{
int v = e[i].to;
if(dis[v] > dis[u] + e[i].w)
{
dis[v] = dis[u] + e[i].w;
if(!vis[v]) h.push(make_pair(dis[v], v));
}
}
}
} int main()
{
ll sum = 0, ans = 0;
scanf("%d%d%d",&n,&m,&c);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
join(a[i].u, a[i].v, a[i].w);
join(a[i].v, a[i].u, a[i].w);
a[i].ind = i;
sum += a[i].w;
}
ans = sum;
dijkstra(1); // 求最短路
for(int i=1;i<=m;i++)
a[i].val = max(dis[a[i].u], dis[a[i].v]);
sort(a+1, a+m+1);
for(int i=1;i<=m;i++)
{
sum -= a[i].w;
ans = min(ans, sum+1ll*a[i].val*c);
// 枚举以一条边为分界点时的答案
}
printf("%lld\n",ans);
return 0;
}

最新文章

  1. web视频添加webvtt字幕测试
  2. 马虎将classname加到了id属性中,造成报错
  3. Java 图片转换为字符图 CharMaps (整理)
  4. 欧拉工程第66题:Diophantine equation
  5. MYSQL获取自增主键【4种方法】
  6. 基于Linux的视频传输系统(上大学时參加的一个大赛的论文)
  7. css中设置div垂直水平居中的方法
  8. 移动端使用rem同时适应安卓ios手机原理解析,移动端响应式开发
  9. 第二十二章 Django会话与表单验证
  10. VS中自定义类模版
  11. 【模拟与阅读理解】Gym - 101954C Rullete
  12. LeetCode Permutations问题详解
  13. 创建genil component
  14. git merge和git rebase
  15. 怎样使用Navicat Premium导出导入mysql数据库
  16. C-C和指针作业题(第一章)
  17. portal商品展示功能逻辑
  18. 【实用代码片段】将json数据绑定到html元素 (转)
  19. iOS 开发如何入门
  20. LogStation 支持浏览器实时查看日志

热门文章

  1. Linux基础第五章 进程控制
  2. STL vector常用API
  3. 大数据 - DWM层 业务实现
  4. Centos7下vim最新版本安装
  5. ssm——spring整理
  6. 如何用 30s 给面试官讲清楚什么是 Session-Cookie 认证
  7. java进阶篇——Stream流编程
  8. 图文并茂--微信小程序,获取用户地理位置信息,并调用腾讯地图API来获取用户具体位置
  9. Flutter框架渲染流程与使用
  10. freeswitch号码黑名单