Utilitarianism (WQS二分)

//https://codeforces.com/group/T43EBH4GgO/contest/437032/problem/M
#include 
using namespace std;
#define all(a) (a).begin(), (a).end()
#define ll long long
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 3e5 + 10;

/*
题意:有一颗带边权的数,从中选出确定的 K 条顶点不相交的边,使这 K 条边的权值和最大

用 WQS二分 将选出恰好 K 条边转化为选任意条边的问题即可 
*/

#define int ll
pair operator + (const pair &a, const pair &b){
    return {a.first + b.first, a.second + b.second};
}

vector> g[N];

pair f[N][2];
void dfs(int x, int fa, int c)
{
    f[x][0] = f[x][1] = {0, 0};
    for(auto &[to, w] : g[x])
    {
        if(to == fa) continue;
        dfs(to, x, c);
        f[x][1] = max(f[x][1] + max(f[to][0], f[to][1]), f[x][0] + f[to][0] + make_pair(w - c, 1));
        f[x][0] = f[x][0] + max(f[to][0], f[to][1]);
    }
}

void solve()
{
    int n, k;
    cin >> n >> k;

    for(int i = 1; i < n; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    int l = -1e13, r = 1e13;
    ll ans = -INF;
    while(l <= r)
    {
        int c = (l + r) / 2;
        dfs(1, -1, c);
        auto [res, cnt] = max(f[1][0], f[1][1]);
        
        if(cnt >= k)
        {
            ans = res + 1ll * k * c;// 要加 k * c,而不是 cnt * c, 当 k 和 k + 1 处斜率相同时,DP出的数量答案是 k + 1 是的,但权值答案与 k 共享
            l = c + 1;
        }
        else
            r = c - 1;
    }

    if(ans == -INF) cout << "Impossible" << '\n';
    else cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--) solve(); 
    return 0;
}

你可能感兴趣的:(算法,WQS二分)