给定 (n) 个点 (m) 条边的无向简单连通图和 (n) 个正整数 (A_i,B_i)。
猫猫可以空降在任意一个点,猫猫经过点 (u) 时至少需要拥有 (A_u) 元,打卡一次需要花费 (B_u) 元。
求在每个点都打卡一次所需的最小初始钱数。
(n,mle 10^5,A_i,B_ile 10^9)。
显然若要重复经过某个点,则在最后经过的这一次打卡是最优的。
先令 (A_i=max(A_i-B_i,0)),表示走出时的最小钱数。要求即为在 (u) 点的任意时刻钱数 (ge A_u)。
令 ((u,v)) 这条边的边权为 (max(A_u,A_v)),猫猫走的边一定是最小生成树上的边。
建出 Kruskal 重构树,考虑树形 dp。设 (f_u) 表示 (u) 子树内的点都打上卡所需的最少初始钱数,(s_u) 表示 (u) 子树内 (B_i) 之和。
若 (u) 是叶节点,则 (f_u=A_u+B_u)。
若 (u) 是非叶节点,显然是先处理一棵子树,然后在 (u) 打卡,再处理另一棵子树:
[f_u=min{s_{ls_u}+max{A_u,f_{rs_u}},s_{rs_u}+max{A_u,f_{ls_u}}}
]
时间复杂度 (O(n+mlog m))。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 222222;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
int n, m, k, A[N], B[N], fa[N], ch[N][2];
LL f[N], s[N];
struct Edge {
int u, v, w;
bool operator < (const Edge &o) const {return w < o.w;}
} e[N];
int getfa(int x){return x == fa[x] ? x : fa[x] = getfa(fa[x]);}
int main(){
read(n); read(m); k = n;
for(int i = 1;i <= n;++ i){
read(A[i]); read(B[i]); fa[i] = i;
A[i] = max(A[i] - B[i], 0);
f[i] = A[i] + B[i]; s[i] = B[i];
} for(int i = 1, u, v;i <= m;++ i){
read(u); read(v);
e[i].u = u; e[i].v = v;
e[i].w = max(A[u], A[v]);
} sort(e + 1, e + m + 1);
for(int i = 1;i <= m;++ i){
int u = getfa(e[i].u), v = getfa(e[i].v);
if(u != v){ ++ k;
fa[u] = fa[v] = fa[k] = k;
A[k] = max(A[u], A[v]); s[k] = s[u] + s[v];
f[k] = min(s[u] + max((LL)A[k], f[v]), s[v] + max((LL)A[k], f[u]));
}
} printf("%lld
", f[k]);
}