[模板] 最短路树

[模板] 最短路树

概念类

定义构建一棵树,使得树上任意不属于根的节点 \(x\)\(dis(root,x)=\)原图走到 \(x\) 的最短路。

显然在跑 \(dij\) 的时候,最后更新到 \(v\) 的边可以在最短路树上。

这棵树的边数为 \(n-1\)

图片来自@Lis~


CF1076D Edge Deletion

题意

给一个 \(n\) 个点, \(m\) 条边的无向简单带权连通图, 要求删边至最多剩余 \(k\) 条边.

定义"好点"是指删边后, 1号节点到它的最短路长度仍然等于原图最短路长度的节点.

最大化删边后的好点个数。

做法

显然,这保留的 \(k\) 条边必须在最短路树上才是最优的。

所以这是一道求最短路树的模板。

首先考虑如何判断一条边是否在最短路树上。

  1. 每次跑最短路的时候记录一下。
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));
}
  1. 直接判断
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;
}
posted @ 2021-08-12 17:22  ¶凉笙  阅读(287)  评论(0编辑  收藏  举报