P6615 Kruskal + 构造

题意

传送门 P6615 草莓

题解

设集合 G G G 中路径最小值为 d d d,那么应尽可能将边权大于等 d d d 的边加入 G G G,在保证集合中路径最小值不变的条件下,最大化点集 S S S。这类似 K r u s k a l Kruskal Kruskal 求生成树的过程,此时点集 S S S 取在路径最小值 d d d 约束下,最大生成森林中的点权和最大的连通分量。

可以观察到,随着路径最小值减小,点集权和单调不降。按照 K r u s k a l Kruskal Kruskal 的思路,对路径权和降序排序,依次处理;那么出现可能解的情况,为点集 S S S 规模恰好变化,即对应的 G G G 中路径最小值最大之时。

朴素的算法需要遍历所有路径,总时间复杂度 O ( N 2 log ⁡ N ) O(N^2\log N) O(N2logN),显然难以胜任。观察 K r u s k a l Kruskal Kruskal 处理的过程,首先处理的路径一定是树的直径,首先加入点集的一定是树的直径的端点。当生成森林变化时,一定处理了以加入节点 u u u 为端点的权值最大的路径,而这条路径一定为从 u u u 到树的直径的某个端点的路径。容易发现, K r u s k a l Kruskal Kruskal 求解过程中只存在一个包含树的直径的端点的联通分量,那么不需要并查集维护连通性。

处理出以各个节点为端点的最长路径,模拟 K r u s k a l Kruskal Kruskal 过程即可。总时间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l, _ = r; i < _; ++i)
typedef long long ll;
const int maxn = 220000;
struct edge
{
    
    
    int to, cost;
};
struct node
{
    
    
    int d, k;
    bool operator<(const node &o) const {
    
     return d > o.d; }
} ns[maxn];
int N, A[maxn], ds[maxn];
vector<edge> G[maxn];

void dfs(int u, int p, int d, int &t, int &mx_ds)
{
    
    
    if (mx_ds < d)
        mx_ds = d, t = u;
    for (auto &e : G[u])
        if (e.to != p)
            dfs(e.to, u, d + e.cost, t, mx_ds);
}

void get_ds(int u, int p, int d)
{
    
    
    ds[u] = max(ds[u], d);
    for (auto &e : G[u])
        if (e.to != p)
            get_ds(e.to, u, d + e.cost);
}

int main()
{
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N;
    rep(i, 0, N) cin >> A[i];
    rep(i, 1, N)
    {
    
    
        int u, v, w;
        cin >> u >> v >> w;
        --u, --v;
        G[u].push_back(edge{
    
    v, w}), G[v].push_back(edge{
    
    u, w});
    }
    int s = 0, t = -1, mx_ds = -1;
    dfs(s, -1, 0, t, mx_ds);
    s = t, t = -1, mx_ds = -1;
    dfs(s, -1, 0, t, mx_ds);
    get_ds(s, -1, 0), get_ds(t, -1, 0);
    rep(i, 0, N) ns[i] = node{
    
    ds[i], i};
    sort(ns, ns + N);
    ll res = 0;
    int sum = A[ns[0].k];
    rep(i, 1, N)
    {
    
    
        sum += A[ns[i].k];
        res = max(res, (ll)sum * ns[i].d);
    }
    cout << res << '\n';
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/119701663