G. Directing Edges

本文探讨了一种在图论背景下优化算法的问题,特别是在处理大型连接图时如何通过边的定向和保留来最大化收益。通过使用深度优先搜索和强连通分量的概念,文章提出了一种高效的算法解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given an undirected connected graph consisting of nnn vertices and mmm edges. kkk vertices of this graph are special.

You have to direct each edge of this graph or leave it undirected. If you leave the iii-th edge undirected, you pay wiw_iwi coins, and if you direct it, you don’t have to pay for it.

Let’s call a vertex saturated if it is reachable from each special vertex along the edges of the graph (if an edge is undirected, it can be traversed in both directions). After you direct the edges of the graph (possibly leaving some of them undirected), you receive ci coins for each saturated vertex iii. Thus, your total profit can be calculated as ∑i∈Sci−∑j∈Uwj∑\limits_{i∈S}c_i−∑\limits_{j∈U}w_jiScijUwj, where SSS is the set of saturated vertices, and UUU is the set of edges you leave undirected.

For each vertex iii, calculate the maximum possible profit you can get if you have to make the vertex iii saturated.

Input
The first line contains three integers nnn, mmm and kkk (2≤n≤3⋅105,n−1≤m≤min(3⋅105,n(n−1)2),1≤k≤n)(2≤n≤3⋅10^5, n−1≤m≤min(3⋅10^5,\frac{n(n−1)}{2}), 1≤k≤n)(2n3105,n1mmin(3105,2n(n1)),1kn).

The second line contains kkk pairwise distinct integers v1,v2,…,vkv_1, v_2, \dots, v_kv1,v2,,vk (1≤vi≤n)(1≤v_i≤n)(1vin) — the indices of the special vertices.

The third line contains nnn integers c1,c2,…,cnc_1, c_2, \dots, c_nc1,c2,,cn (0≤ci≤109)(0≤c_i≤10^9)(0ci109).

The fourth line contains mmm integers w1,w2,…,wmw_1, w_2, \dots, w_mw1,w2,,wm (0≤wi≤109)(0≤w_i≤10^9)(0wi109).

Then mmm lines follow, the iii-th line contains two integers xix_ixi and yiy_iyi (1≤xi,yi≤n,xi≠yi)(1≤x_i,y_i≤n, x_i≠y_i)(1xi,yin,xi=yi) — the endpoints of the iii-th edge.

There is at most one edge between each pair of vertices.

Output
Print nnn integers, where the iii-th integer is the maximum profit you can get if you have to make the vertex iii saturated.

Examples

input
3 2 2
1 3
11 1 5
10 10
1 2
2 3
output
11 2 5 
input
4 4 4
1 2 3 4
1 5 7 8
100 100 100 100
1 2
2 3
3 4
1 4
output
21 21 21 21 

Note
Consider the first example:

the best way to make vertex 111 saturated is to direct the edges as 2→1,3→22→1, 3→221,32; 111 is the only saturated vertex, so the answer is 111111;
the best way to make vertex 222 saturated is to leave the edge 1−21−212 undirected and direct the other edge as 3→23→232; 111 and 222 are the saturated vertices, and the cost to leave the edge 1−21−212 undirected is 101010, so the answer is 222;
the best way to make vertex 333 saturated is to direct the edges as 2→32→323, 1→21→212; 333 is the only saturated vertex, so the answer is 555.
The best course of action in the second example is to direct the edges along the cycle: 1→2,2→3,3→41→2, 2→3, 3→412,23,34 and 4→14→141. That way, all vertices are saturated.

首先,对于一个边双连通分量,由于任意两点之间至少存在两条边不重复路径,因此可以通过将其所有边修改为有向边使其成为一个强连通分量。对于一个强连通分量,只要其中一个点饱和,则其余点都饱和,因此可以进行e−DCCe-DCCeDCC缩点,将其转化为树上问题。
缩点后,以某个关键节所在点为根,如果以某个点为根的子树中不含关键节点,则这棵子树无需向外连边,即子树上的所有点都只连一条到子节点的有向边即可,不需要花费连无向边的费用,因此可以将该子树上的点全部合并到根节点的父节点上。最终构造出来的树的根节点和所有叶子节点都含关键节点。
这里解释一下为什么要以某个关键节所在点为根:因为在后面树形dpdpdp的时候首先默认了对于当前dpdpdp的点,其子树上所有点都能到达它,而不含关键节点的点不需要其他点都能到达它如果加反边会增加不必要的花费,结果不是最优。
设边双连通分量SSS缩点为xxx,则val[x]=∑i∈Sc[i]val[x]=\sum\limits_{i∈S}c[i]val[x]=iSc[i]
f[x]f[x]f[x]为以xxx为根的子树中如果饱和点包含xxx,那么最大收益为f[x]f[x]f[x]
这里的饱和点的含义是:如果xxx为根节点则所有以xxx为根的子树中所有的关键节点都能到达该点,否则xxx为整个树上的饱和点,但是算收益时只减去子树内的边(后面转移的时候会考虑上其余边)。
状态转移方程为:
f[x]=val[x]+∑y∈sonmax⁡(0,f[y]−wx,y)f[x]=val[x]+\sum_{y\in son}\max(0,f[y]-w_{x,y}) f[x]=val[x]+ysonmax(0,f[y]wx,y)
状态转移方程的含义为首先假设子树上的所有点都能到达xxx,那么xxx为饱和点,此时收益为val[x]val[x]val[x] ,如果对于子节点yyy,如果以yyy为根的子树中的一些点也想成为饱和点,那么需要在当前收益上加上f[y]−wx,yf[y]-w_{x,y}f[y]wx,y,考虑到最大收益,因此只加为正的时候的,即 max⁡(0,f[y]−wx,y)\max(0,f[y]-w_{x,y})max(0,f[y]wx,y)
最终f[root]f[root]f[root]为保证根节点是饱和点时的最大收益。
由于题目中要求求出对于每个点,保证该点为饱和点时的最大收益,因此还需要进行换根dpdpdp
状态转移方程为:
f[y]=f[y]+max⁡(0,f[x]−max⁡(0,f[y]−wx,y)−wx,y)f[y]=f[y]+\max (0,f[x]-\max (0,f[y]-w_{x,y})-w_{x,y})f[y]=f[y]+max(0,f[x]max(0,f[y]wx,y)wx,y)
这里xxxyyy的父节点。保证yyy点为饱和点时的最大收益,为以yyy为根节点时的最大收益加上父节点xxxf[y]f[y]f[y]的贡献再减去wx,yw_{x,y}wx,y,即max⁡(0,f[x]−max⁡(0,f[y]−wx,y)−wx,y)\max (0,f[x]-\max (0,f[y]-w_{x,y})-w_{x,y})max(0,f[x]max(0,f[y]wx,y)wx,y)。而父节点xxxf[y]f[y]f[y]的贡献为保证xxx点为饱和点时的最大收益减去以yyy为根的子树对其的贡献,即max⁡(0,f[y]−wx,y)−wx,y\max (0,f[y]-w_{x,y})-w_{x,y}max(0,f[y]wx,y)wx,y
时间复杂度为O(n)O(n)O(n)

#include<bits/stdc++.h>

#define repi(i, a, b) for(register int i=a;i<=b;++i)
#define ll long long
#define ce(i, r) i==r?'\n':' '
using namespace std;

inline int qr() {
    int f = 0, fu = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')fu = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        f = (f << 3) + (f << 1) + c - 48;
        c = getchar();
    }
    return f * fu;
}

const int N = 3e5 + 10, M = 3e5 + 10;
int head[N], ver[M << 1], Next[M << 1], edge[M << 1], tot = 1;
int hc[N], vc[M << 1], nc[M << 1], ec[M << 1], tc, dcc;
int n, m, k;

inline void add(int x, int y, int z) {
    ver[++tot] = y;
    Next[tot] = head[x];
    edge[tot] = z;
    head[x] = tot;
}

struct E_DCC {
    int dfn[N], low[N], c[N];
    int num;
    bool bridge[M << 1];

    void tarjan(int x, int in_edge) {
        dfn[x] = low[x] = ++num;
        for (int i = head[x]; i; i = Next[i]) {
            int y = ver[i];
            if (!dfn[y]) {
                tarjan(y, i);
                low[x] = min(low[x], low[y]);
                if (low[y] > dfn[x])
                    bridge[i] = bridge[i ^ 1] = true;
            } else if (i != (in_edge ^ 1))
                low[x] = min(low[x], dfn[y]);
        }
    }

    inline void solve() {
        repi(i, 1, n)if (!dfn[i])tarjan(i, 0);
        e_dcc(), build();
    }


    void dfs(int x) {
        c[x] = dcc;
        for (int i = head[x]; i; i = Next[i]) {
            int y = ver[i];
            if (c[y] || bridge[i]) continue;
            dfs(y);
        }
    }

    inline void e_dcc() {
        dcc = 0;
        repi(i, 1, n)if (!c[i])++dcc, dfs(i);
    }

    void add_c(int x, int y, int z) {
        vc[++tc] = y, nc[tc] = hc[x], ec[tc] = z, hc[x] = tc;
    }

    inline void build() {
        repi(i, 1, n)hc[i] = 0;
        tc = 1;
        repi(i, 2, tot) {
            int x = ver[i ^ 1], y = ver[i], z = edge[i];
            if (c[x] == c[y]) continue;
            add_c(c[x], c[y], z);
        }
    }
} E;

int c[N], w[M], v[N], top[N];
ll val[N], f[N];
bool key[N];

void dfs1(int x, int fa) {
    for (int i = hc[x]; i; i = nc[i]) {
        int y = vc[i];
        if (y == fa)continue;
        dfs1(y, x), key[x] |= key[y];
    }
}

void dfs2(int x, int fa) {
    top[x] = key[x] ? x : top[fa];
    for (int i = hc[x]; i; i = nc[i]) {
        int y = vc[i];
        if (y == fa)continue;
        dfs2(y, x);
    }
}

void dfs3(int x, int fa) {
    f[x] = val[x];
    for (int i = hc[x]; i; i = nc[i]) {
        int y = vc[i], z = ec[i];
        if (y == fa || !key[y])continue;
        dfs3(y, x), f[x] += max(0ll, f[y] - z);
    }
}

void dfs4(int x, int fa) {
    for (int i = hc[x]; i; i = nc[i]) {
        int y = vc[i], z = ec[i];
        if (y == fa || !key[y])continue;
        f[y] += max(0ll, f[x] - max(0ll, f[y] - z) - z), dfs4(y, x);
    }
}

int main() {
    n = qr(), m = qr(), k = qr();
    repi(i, 1, k)v[i] = qr();
    repi(i, 1, n)c[i] = qr();
    repi(i, 1, m)w[i] = qr();
    repi(i, 1, m) {
        int x = qr(), y = qr();
        add(x, y, w[i]), add(y, x, w[i]);
    }
    E.solve();
    repi(i, 1, n)val[E.c[i]] += c[i];
    repi(i, 1, k)key[E.c[v[i]]] = true;
    int r = E.c[v[1]];
    dfs1(r, 0), dfs2(r, 0);
    repi(i, 1, dcc)if (!key[i])val[top[i]] += val[i];
    repi(i, 1, n)E.c[i] = top[E.c[i]];
    dfs3(r, 0), dfs4(r, 0);
    repi(i, 1, n)printf("%lld%c", f[E.c[i]], ce(i, n));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值