修改图中的边权

Posted 929code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了修改图中的边权相关的知识,希望对你有一定的参考价值。

给你一个n节点的无向带权连通图,同时告诉你边的端点和权值
对于部分权为-1的边,可以进行修改为任意值,最后使得初始点到目标点最短距离为target

1. Dijkstra

邻接表存储
class Solution 
public:
    vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>> &edges, int source, int destination, int target) 
        vector<pair<int, int>> g[n]; //邻接表
        for (int i = 0; i < edges.size(); i++)  // 遍历所有边
            int x = edges[i][0], y = edges[i][1];
            g[x].emplace_back(y, i);
            g[y].emplace_back(x, i); // 建图,额外记录边的编号,用于后面修改和返回
        


        int dis[n][2], delta, vis[n]; 
    
        memset(dis, 0x3f, sizeof(dis));
        dis[source][0] = dis[source][1] = 0; //初始化初始节点距离
        auto dijkstra = [&](int k)  // 这里 k 表示第一次/第二次
            memset(vis, 0, sizeof(vis)); //初始都未访问
            for (;;) //因为是连通图,跳出条件为找到目标值
                int x = -1; //用于贪心找点
                for (int i = 0; i < n; ++i) //遍历所有点
                    if (!vis[i] && (x < 0 || dis[i][k] < dis[x][k])) //找未标记的最近点
                        x = i;
                if (x == destination) // 起点 source 到终点 destination 的最短路已确定
                    return;
                vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度
                for (auto [y, id]: g[x])   //遍历x的相邻边
                    int weight = edges[id][2];  //获取边的权值,这里要修正,但不修改,所以需要暂存,而不是直接使用权值去更新
                    if (weight == -1)  weight = 1; // -1 改成 1,第一次不直接在edge上改,防止丢失修改目标

                    if (k == 1 && edges[id][2] == -1)  //对于可以进行修正的边
                        // 第二次 Dijkstra,修正,是满足目标值
                        int w = delta + dis[y][0] - dis[x][1]; //修正的权值
                        if (w > weight)
                            edges[id][2] = weight = w; // 直接在 edges 上修改
                    

                    // 更新最短路
                    dis[y][k] = min(dis[y][k], dis[x][k] + weight);
                
            
        ;

        dijkstra(0);
        delta = target - dis[destination][0]; //还需要增加的值
        if (delta < 0) // -1 全改为 1 时,最短路比 target 还大
            return ;

        dijkstra(1);
        if (dis[destination][1] < target) // 最短路无法再变大,无法达到 target
            return ;

        for(auto&edge:edges) //按题目要求修改所有-1权值
            if(edge[2]==-1) edge[2] = 1;


        return edges;
    
;

邻接矩阵

POJ 2763 /// 基于边权的树链剖分

题目大意:

给定n个结点,有n-1条无向边,给定每条边的边权

两种操作,第一种:求任意两点之间路径的权值和,第二种:修改树上一点的权值。

 

因为是一棵树,可以直接把 u点和v点间(假设u为父节点,v为子节点)的边 的边权往下给v点

这样就转换成了点权,那么此时查询 u点到v点之间路径的权值和 的话

由于u点存有 u的父节点到u 的边权,所以应该查询的是 u到v的路径上 的第二个点到v的权值和

修改查询树上两结点间路径长度的函数
int queryPath(int x,int y){ }
中求最后一步的部分

/// 点权版本
if(p[x]>p[y]) swap(x,y);
return ans+query(p[x],p[y],1,pos,1);
/// 边权版本
if(x==y) return ans;
if(dep[x]>dep[y]) swap(x,y);
return ans+query(p[son[x]],p[y],root);

 

技术分享图片
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,n,1

const int maxn=1e5+5;
int n,q,s;

struct QTree {
    struct EDGE { int to,ne; }e[maxn<<1];
    int head[maxn], tot;
    void add(int u,int v) {
        e[tot].to=v;
        e[tot].ne=head[u];
        head[u]=tot++;
    }

    int fa[maxn], son[maxn], dep[maxn], num[maxn];
    int top[maxn], p[maxn], fp[maxn], pos;

    int sumT[maxn<<2];

    void init() {
        tot=1; mem(head,0);
        pos=0; mem(son,0);
    }

// --------------------以下是线段树-------------------------

    void pushup(int rt) {
        sumT[rt]=sumT[rt<<1]+sumT[rt<<1|1];
    }
    void build(int l,int r,int rt) {
        if(l==r) {
            sumT[rt]=0; return ;
        }
        int m=(l+r)>>1;
        build(lson), build(rson);
        pushup(rt);
    }
    void update(int k,int w,int l,int r,int rt) {
        if(l==r) {
            sumT[rt]=w; return;
        }
        int m=(l+r)>>1;
        if(k<=m) update(k,w,lson);
        else update(k,w,rson);
        pushup(rt);
    }
    int query(int L,int R,int l,int r,int rt) {
        if(L<=l && r<=R) return sumT[rt];
        int m=(l+r)>>1, res=0;
        if(L<=m) res+=query(L,R,lson);
        if(R>m) res+=query(L,R,rson);
        return res;
    }

// --------------------以上是线段树-------------------------



// --------------------以下是树链剖分-------------------------

    void dfs1(int u,int pre,int d) {
        dep[u]=d; fa[u]=pre; num[u]=1;
        for(int i=head[u];i;i=e[i].ne) {
            int v=e[i].to;
            if(v!=fa[u]) {
                dfs1(v,u,d+1);
                num[u]+=num[v];
                if(!son[u] || num[v]>num[son[u]])
                    son[u]=v;
            }
        }
    }
    void dfs2(int u,int sp) {
        top[u]=sp; p[u]=++pos; fp[p[u]]=u;
        if(!son[u]) return;
        dfs2(son[u],sp);
        for(int i=head[u];i;i=e[i].ne) {
            int v=e[i].to;
            if(v!=son[u] && v!=fa[u])
                dfs2(v,v);
        }
    }
    int queryPath(int x,int y) {
        int ans=0;
        int fx=top[x], fy=top[y];
        while(fx!=fy) {
            if(dep[fx]>=dep[fy]) {
                ans+=query(p[fx],p[x],root);
                x=fa[fx];
            } else {
                ans+=query(p[fy],p[y],root);
                y=fa[fy];
            }
            fx=top[x], fy=top[y];
        }
        if(x==y) return ans;
        if(dep[x]>dep[y]) swap(x,y);
        return ans+query(p[son[x]],p[y],root);
    }

// --------------------以上是树链剖分-------------------------

    void initQTree() {
        dfs1(1,0,0);
        dfs2(1,1);
        build(root);
    }
}T;
int E[maxn][3];

int main()
{
    while(~scanf("%d%d%d",&n,&q,&s)) {
        T.init();
        for(int i=1;i<n;i++) {
            int u,v,w; scanf("%d%d%d",&u,&v,&w);
            E[i][0]=u, E[i][1]=v, E[i][2]=w;
            T.add(u,v); T.add(v,u);
        }
        T.initQTree();
        for(int i=1;i<n;i++) {
            if(T.dep[E[i][0]]>T.dep[E[i][1]])
                swap(E[i][0],E[i][1]);
            T.update(T.p[E[i][1]],E[i][2],root);
        }
        while(q--) {
            int op; scanf("%d",&op);
            if(op) { 
                int k,w; scanf("%d%d",&k,&w);
                T.update(T.p[E[k][1]],w,root);
            } else { 
                int v; scanf("%d",&v);
                printf("%d
",T.queryPath(s,v));
                s=v;
            }
        }
    }

    return 0;
}
View Code

 

以上是关于修改图中的边权的主要内容,如果未能解决你的问题,请参考以下文章

CF716D Complete The Graph

POJ 2763 /// 基于边权的树链剖分

Codeforces 1038E Maximum Matching

POJ3237Tree(树链剖分)

luogu4234 最小差值生成树

CF E2 - Daleks' Invasion (medium) (LCA求两点树上路径上的最大边权)