嘘~ 正在从服务器偷取页面 . . .

CF76A Gift 题解


CF76A Gift 题解

题目链接:CF76A Gift

题意

一张 \(N\) 个点 \(M\) 条边的无向图,每条边有两个属性 \((g_i, s_i)\)

给定 \(G, S\),求一棵图的生成树 \(T\),使得 \(G \times \max(g_i) + S \times \max (s_i)\) 最小。(\(i\in T\)

注意:图可能包含重边和自环。

数据范围

\(2 \le N \le 200,~1\le M \le 50000,~1 \le G,S \le 10^9\)

建议先写 洛谷P2387 [NOI2014] 魔法森林

考虑将边化成点,然后按 \(g_i\) 排序,依次加入每条边,

同时维护 \(s_i\) 的最小生成树,每次图连通时更新答案。

维护 \(s_i\) 的最小生成树时,如果两个点不连通直接合并,否则我们需要找到所成环上的 \(s_i\) 最大值。

考虑用 LCT 维护链上的 \(s_i\) 最大值即可。更新答案的话,可以开两个 multiset 维护边集。

时间复杂度 \(\mathcal{O}(m \log n)\) ,轻松通过本题。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e5 + 15))

int n, m, G, S, res = INF;
struct Edge {int u, v, g, s; } e[N];
bool operator<(const Edge& a, const Edge& b) { return a.g < b.g; }
namespace LCT
{
    struct node
    {
        int ch[2], num, fa, ansg, anss, tag;
    }t[N];
    struct MG { int x; };
    bool operator<(const MG& u, const MG& v) { return e[u.x].g > e[v.x].g; }
    struct MS { int x; };
    bool operator<(const MS& u, const MS& v) { return e[u.x].s > e[v.x].s; }
    multiset<MG> mg; multiset<MS> ms;
    struct dsu
    {
        int f[N], sz[N];
        void init(int n) { rep(i, 1, n) { f[i] = i, sz[i] = 1; }}
        int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
        void merge(int u, int v) 
        {
            int x = find(u), y = find(v);
            if(sz[x] > sz[y]) swap(x, y); sz[y] += sz[x]; f[x] = y;
        }
    }D;
    #define isroot(x) ((t[t[x].fa].ch[0] != x)&&(t[t[x].fa].ch[1] != x))
    void pushr(int x)
    {
        swap(t[x].ch[0], t[x].ch[1]);
        t[x].tag ^= 1;
    }
    int Maxg(int x, int y) { return e[x].g > e[y].g ? x : y; }
    int Maxs(int x, int y) { return e[x].s > e[y].s ? x : y; }
    void push_up(int at)
    {
        t[at].ansg = Maxg(at, Maxg(t[t[at].ch[0]].ansg, t[t[at].ch[1]].ansg));
        t[at].anss = Maxs(at, Maxs(t[t[at].ch[0]].anss, t[t[at].ch[1]].anss));
    }
    void push_down(int x)
    {
        if(t[x].tag)
        {
            if(t[x].ch[0]) pushr(t[x].ch[0]);
            if(t[x].ch[1]) pushr(t[x].ch[1]);
            t[x].tag = 0;
        }
    }
    void push_all(int x)
    {
        if(!isroot(x)) push_all(t[x].fa);
        push_down(x);
    }
    void rotate(int x)
    {
        int y = t[x].fa;
        int z = t[y].fa;
        int k = (t[y].ch[1] == x);
        if(!isroot(y)) t[z].ch[t[z].ch[1] == y] = x;
        t[x].fa = z;
        t[y].ch[k] = t[x].ch[k ^ 1];
        t[t[x].ch[k ^ 1]].fa=y;
        t[x].ch[k ^ 1]=y;
        t[y].fa = x;
        push_up(y);
        push_up(x);
    }
    void splay(int x)
    {
        push_all(x);
        while(!isroot(x))
        {
            int y = t[x].fa;
            int z = t[y].fa;
            if(!isroot(y)) (t[z].ch[1] == y) ^ (t[y].ch[1] == x)? rotate(x) : rotate(y);
            rotate(x);
        }
    }
    void access(int x)
    {
        for(int y = 0; x; y = x, x = t[x].fa) {
            splay(x), t[x].ch[1] = y,push_up(x);
        }
    }
    void make_root(int x)
    {
        access(x); splay(x);
        pushr(x);
    }
    int find_root(int x)
    {
        access(x); splay(x);
        while(t[x].ch[0]) { push_down(x), x = t[x].ch[0]; }
        splay(x);
        return x;
    }
    void split(int x,int y)
    {
        make_root(x);
        access(y); splay(y);
    }
    void link(int x,int y)
    {
        make_root(x);
        if(find_root(y) != x) t[x].fa = y;
    }
    void cut(int x,int y)
    {
        make_root(x);
        if(find_root(y) == x && t[y].fa == x && !t[y].ch[0])
        {
            t[x].ch[1] = t[y].fa = 0;
            push_up(x);
        }
    }
}using namespace LCT;
void solve(int i)
{
    if(D.find(e[i].u) != D.find(e[i].v))
    {
        link(e[i].u + m, i); link(e[i].v + m, i);
        mg.insert({i}); ms.insert({i}); D.merge(e[i].u, e[i].v);
    }else
    {
        split(e[i].u + m, e[i].v + m);
        if(e[t[e[i].v + m].anss].s > e[i].s)
        {
            int tmp = t[e[i].v + m].anss;
            cut(e[tmp].u + m, tmp); cut(e[tmp].v + m, tmp);
            mg.erase(mg.find({tmp})); ms.erase(ms.find({tmp}));
            link(e[i].u + m, i); link(e[i].v + m, i);
            mg.insert({i}); ms.insert({i});
        }
    }
    if(D.sz[D.find(1)] == n) down(res, e[(*mg.begin()).x].g * G + e[(*ms.begin()).x].s * S);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> G >> S;
    rep(i, 1, m) cin >> e[i].u >> e[i].v >> e[i].g >> e[i].s;
    sort(e + 1, e + 1 + m); D.init(n);
    rep(i, 1, m) solve(i);
    cout << (res >= INF ? -1 : res) << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/9t2ycdyz


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录