题集二

图论部分

最短路径树

定义为从原点出发,到每个节点的最短路径所构成的树。

可以用 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;
}
posted @ 2024-07-13 08:18  view3937  阅读(11)  评论(0编辑  收藏  举报
Title