[模板] 最短路树
[模板] 最短路树
概念类
定义构建一棵树,使得树上任意不属于根的节点 \(x\),\(dis(root,x)=\)原图走到 \(x\) 的最短路。
显然在跑 \(dij\) 的时候,最后更新到 \(v\) 的边可以在最短路树上。
这棵树的边数为 \(n-1\) 。
图片来自@Lis~
题意
给一个 \(n\) 个点, \(m\) 条边的无向简单带权连通图, 要求删边至最多剩余 \(k\) 条边.
定义"好点"是指删边后, 1号节点到它的最短路长度仍然等于原图最短路长度的节点.
最大化删边后的好点个数。
做法
显然,这保留的 \(k\) 条边必须在最短路树上才是最优的。
所以这是一道求最短路树的模板。
首先考虑如何判断一条边是否在最短路树上。
- 每次跑最短路的时候记录一下。
if(!vis[v] && dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
pre[v]=id;//记录边的id
q.push(mp(dis[v],v));
}
- 直接判断
if(dis[v]==dis[u]+e[i].w){
//...
}
目测两种都可以过 CF 评测这么神奇
显然一棵最短路树可以有很多种形态,但是对这道题来说不用犹豫。
所以可以直接这么写:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
}
return fl?-x:x;
}
const int maxn = 3e5 + 10;
int head[maxn],cnt=0;
struct edge{
int to,nxt;LL w;
int id;
}e[maxn<<1];
inline void link(int u,int v,LL w){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;
}
bool vis[maxn];
int n,m,k,tot;
LL dis[maxn];
int pre[maxn];
#include <queue>
#define mp make_pair
#define Pair pair<long long,int>
void dij(int s){
priority_queue <Pair,vector<Pair>,greater<Pair> > q;
memset(dis,0x3f,sizeof dis);
dis[s]=0LL;q.push(mp(0,s));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,id=e[i].id;
if(!vis[v] && dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
pre[v]=id;q.push(mp(dis[v],v));
}
}
}
}
#define read() read<int>()
int ans[maxn],top=0;
void dfs(int u){
if(tot>=k)return ;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,id=e[i].id;
if(vis[v])continue;
if(dis[v]==dis[u]+e[i].w){
ans[++top]=id;
tot++;
dfs(v);
if(tot>=k)return ;
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
int u,v;LL w;
scanf("%d%d%lld",&u,&v,&w);
link(u,v,w);e[cnt].id=i;
link(v,u,w);e[cnt].id=i;
}
dij(1);
//
//cerr<<"dises:\n";
//for(int i=1;i<=n;i++)printf("%lld ",dis[i]);
//cerr<<endl;
//
memset(vis,false,sizeof vis);
printf("%d\n",min(n-1,k));
dfs(1);
for(int i=1;i<=top;i++)printf("%d ",ans[i]);
return 0;
}