题集二
图论部分
最短路径树
定义为从原点出发,到每个节点的最短路径所构成的树。
可以用 Dijkstra 实现,每一轮松弛时,若 \(v\) 能被 \(u\) 更新,则当前最短路径就包含 \(u\to v\),据此保存最后一次更新的边,然后就可以建树了。
Edge Deletion
给一个\(n\)个点,\(m\)条边的无向简单带权连通图, 要求删边至最多剩余\(k\)条边.
定义"好点"是指删边后, 1号节点到它的最短路长度仍然等于原图最短路长度的节点.
最大化删边后的好点个数.
输出第一行一个整数\(e, (0 \le e \le k)\), 需要保留的边数.
接下来一行\(e\)个整数, 保留的边的编号, 边是按输入顺序编号的, 但输出可以以任意顺序.
\(n, m \le 3 \times 10^5\).
考虑好点的定义,如果该点在最短路径树上,则该点是好点,因为此时它有且仅有一条最短路。
于是就好办了,先跑 Dijkstra 建出最短路径树,若 \(k\ge n-1\),则保留最短路径树上的边即可,此时好点为 \(n\) 个;若 \(k< n-1\),则可以 DFS 选择 \(k\) 个节点即可,时间复杂度 \(O(m\log m)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=6e5+3;
const int inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
struct edge{
int v,w,id;
edge(int v=0,int w=0,int id=0): v(v),w(w),id(id){}
};
vector<edge>e[maxn],g[maxn];
int n,m,k;
struct dist{
int dis,id;
bool operator<(const dist &o)const{return dis>o.dis;}
};
priority_queue<dist>q;
int dis[maxn],vis[maxn],ans[maxn],from[maxn],to[maxn];
void dijkstra(){
for(int i=1;i<=n;i++) dis[i]=inf;
q.push({dis[1]=0,1});
while(!q.empty()){
dist u=q.top();
q.pop();
if(!vis[u.id]){
vis[u.id]=1;
for(edge v:e[u.id]){
if(dis[v.v]>dis[u.id]+v.w){
from[v.v]=v.id/2;
to[v.v]=u.id;
dis[v.v]=dis[u.id]+v.w;
q.push({dis[v.v],v.v});
}
}
}
}
for(int i=2;i<=n;i++){
ans[from[i]]=1;
g[i].emplace_back(edge(to[i],1,from[i]*2));
g[to[i]].emplace_back(edge(i,1,from[i]*2+1));
}
}
int now=0;
void dfs(int u,int fa){
for(edge v:g[u]) if(v.v!=fa)
if(now<k){
now++;
ans[v.id/2]=2;
dfs(v.v,u);
}
}
signed main(){
cin>>n>>m>>k;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w;
e[u].emplace_back(edge(v,w,i*2));
e[v].emplace_back(edge(u,w,i*2+1));
}
dijkstra();
if(k>=n-1){
cout<<n-1<<'\n';
for(int i=1;i<=m;i++)
if(ans[i]) cout<<i<<' ';
return 0;
}
dfs(1,0);
int cnt=0;
for(int i=1;i<=m;i++)
if(ans[i]==2) cnt++;
cout<<cnt<<'\n';
for(int i=1;i<=m;i++)
if(ans[i]==2) cout<<i<<' ';
return 0;
}