首页 > 其他分享 >CF603E 题解

CF603E 题解

时间:2024-08-30 17:14:13浏览次数:4  
标签:int 题解 void pos edge son fa CF603E

题意

给定一个 \(n\) 个结点的无向图,初始没有边。

接下来有 \(m\) 个询问,每次向图中加入一条连接 \((u, v)\) 权值为 \(w\) 的边。

每次加边后,查询是否存在一个边集 \(E\),满足当图中只有 \(E\) 中的边时,所有点的度数均为奇数。

同时你还要最小化 \(\max\limits_{(u, v, w) \in E} w\) 。

考虑先构造 \(E\),有结论:

  • 若图中所有联通块大小均为偶数,则存在子图使得所有点度数为奇数。

证明:只需证明联通块数量为 \(1\) 时的情况。若存在一条边 \((x, y)\) 满足断掉 \((x, y)\) 形成的两个联通块大小为偶数,则断掉 \((x, y)\) 并转化为子问题。否则以任意结点 \(rt\) 为根,设 \(siz(x)\) 表示 \(x\) 结点的子树大小,对每个节点讨论(下面设 \(\operatorname{odd}, \operatorname{even}\) 分别表示奇数和偶数):

  1. 若 \(x \ne rt\),则 \(siz(x)\) 与 \(n - siz(x)\) 均为奇数,由于 \(siz(son_x)\) 也是奇数,所以 \(x\) 一定有偶数个儿子,因为 \(\operatorname{even} \times \operatorname{odd} + 1 = \operatorname{odd}\),并且 \(x\) 与其父亲连有一边,所以 \(deg_x\) 为奇数。
  2. 若 \(x = rt\),由于 \(siz(son_x)\) 为奇数,且 \(\operatorname{odd} \times \operatorname{odd} + 1 = \operatorname{even}\),所以 \(rt\) 一定有奇数个儿子,\(deg_{rt}\) 为奇数。

综上所述,结论得证。

考虑用 LCT 维护,开一个集合维护现在加进来的边,每次新加边后看看能不能少几条集合里的边即可。时间复杂度 \(O(n \log n)\)。

代码
#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int N = 5E5 + 1;
int n, m;

struct Edge {
    int fr, to;
    int len;
} edge[N];

struct LinkCutTree {
    int son[N][2], rev[N], pos[N], fa[N], val[N], sz[N], vsz[N], id[N];

    bool checkson(int x) {
        return son[fa[x]][1] == x;
    }
    bool checkroot(int x) {
        return son[fa[x]][0] != x && son[fa[x]][1] != x;
    }
    void pull(int x) {
        sz[x] = sz[son[x][0]] + 1 + sz[son[x][1]] + vsz[x];
        pos[x] = id[x];
        if (edge[pos[son[x][0]]].len > edge[pos[x]].len) {
            pos[x] = pos[son[x][0]];
        }
        if (edge[pos[son[x][1]]].len > edge[pos[x]].len) {
            pos[x] = pos[son[x][1]];
        }
    }
    void apply(int x) {
        std::swap(son[x][0], son[x][1]);
        rev[x] ^= 1;
    }
    void push(int x) {
        if (rev[x]) {
            apply(son[x][0]);
            apply(son[x][1]);
            rev[x] = 0;
        }
    }

    void rotate(int x) {
        int y = fa[x], z = fa[y], chy = checkson(y), chx = checkson(x);
        if (!checkroot(y)) {
            son[z][chy] = x;
        }
        fa[x] = z;

        fa[son[y][chx] = son[x][!chx]] = y;
        son[fa[y] = x][!chx] = y;

        pull(y);
        pull(x);
    }
    void update(int x) {
        if (!checkroot(x)) {
            update(fa[x]);
        }
        push(x);
    }
    void splay(int x) {
        update(x);
        while (!checkroot(x)) {
            if (!checkroot(fa[x])) {
                rotate(checkson(x) != checkson(fa[x]) ? x : fa[x]);
            }
            rotate(x);
        }
        pull(x);
    }

    void access(int x) {
        for (int y = 0; x; x = fa[y = x]) {
            splay(x);
            vsz[x] -= sz[y] - sz[son[x][1]];
            son[x][1] = y;
            pull(x);
        }
    }
    void makeroot(int x) {
        access(x);
        splay(x);
        apply(x);
    }

    int findroot(int x) {
        access(x);
        splay(x);

        push(x);
        while (son[x][0]) {
            x = son[x][0];
            push(x);
        }

        return splay(x), x;
    }
    void split(int x, int y) {
        makeroot(x);
        access(y);
        splay(y);
    }

    void link(int u, int v) {
        makeroot(u);
        if (findroot(v) != u) {
            fa[u] = v;
            vsz[v] += sz[u];
            pull(v);
        }
    }
    void cut(int u, int v) {
        split(u, v);
        if (!son[u][1] && son[v][0] == u) {
            son[v][0] = 0;
            fa[u] = 0;
            pull(v);
        }
    }

    void addEdge(int i) {
        link(edge[i].fr, n + i);
        link(n + i, edge[i].to);
    }
    void cutEdge(int i) {
        cut(edge[i].fr, n + i);
        cut(edge[i].to, n + i);
        vsz[i + n] = 0;
    }

    bool same(int u, int v) {
        return findroot(u) == findroot(v);
    }
    int query_route(int u, int v) {
        split(u, v);
        return pos[v];
    }
} t;

int check(int x) {
    return ((t.sz[x] + 1) / 2) % 2 == 1;
}

int odd_size;
std::set<std::array<int, 2>> edges;
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::cin >> n >> m;
    odd_size = n;
    for (int i = 1; i <= n + m; ++i) {
        if (i <= n) {
            t.id[i] = 0;
        } else {
            t.id[i] = i - n;
        }
        t.pull(i);
    }

    for (int i = 1; i <= m; ++i) {
        std::cin >> edge[i].fr >> edge[i].to >> edge[i].len;
    }

    for (int i = 1; i <= m; ++i) {
        int u = edge[i].fr, v = edge[i].to, w = edge[i].len;

        if (!t.same(u, v)) {
            t.makeroot(u);
            odd_size -= check(u);
            t.makeroot(v);
            odd_size -= check(v);
            t.addEdge(i);
            t.makeroot(u);
            odd_size += check(u);
        } else {
            int p = t.query_route(u, v);

            if (w < edge[p].len) {
                t.cutEdge(p);
                t.addEdge(i);

                edges.erase(edges.find({edge[p].len, p}));
            } else {
                if (odd_size > 0) {
                    std::cout << -1 << "\n";
                } else {
                    std::cout << (*--edges.end())[0] << "\n";
                }
                continue;
            }
        }

        edges.insert({w, i});
        if (odd_size > 0) {
            std::cout << -1 << "\n";
            continue;
        }

        while (true) {
            auto [dis, p] = *--edges.end();
            int a = edge[p].fr;
            int b = edge[p].to;

            t.split(a, b);
            if (check(a)) {
                break;
            }

            t.cutEdge(p);
            edges.erase(*--edges.end());
        }

        std::cout << (*--edges.end())[0] << "\n";
    }

    return 0;
}

标签:int,题解,void,pos,edge,son,fa,CF603E
From: https://www.cnblogs.com/CTHOOH/p/18388291

相关文章

  • 【Mysql】mysql count主键字段很慢超时 执行计划Select tables optimized away ,最终调
     背景: mysql表 主键字段count,速度很慢,耗时将近30s   从执行计划可以看出:explainSELECTCOUNT(rule_id)ASdataCountFROM`sku_safe_stock_rule`;   原理分析:SelecttablesoptimizedawaySELECT操作已经优化到不能再优化了(MySQL根本没有遍历......
  • CF891E Lust 题解
    题目链接点击打开链接题目解法会不了\(egf\)/ll我们把贡献变成\(\prod\limits_{j\neqi}a_j=\prod\limits_{j=1}^na_j-\prod\limits_{j=1}^n(a_i-[i=j])\)即答案为一开始的乘积\(-\)\(k\)次操作之后所有数乘积的期望因为有顺序,所以用\(egf\)的形式表示最后乘积的期......
  • UVA12421 (Jiandan) Mua (I) - Lexical Analyzer题解
    没什么废话、超级珂爱的大模拟。本蒟蒻写了2h才过。其实就是按题意模拟即可,不需要什么高深的算法。本人就是错在了符号中的“=”,因为如果是连续的两个等于号,只能输出“==”,而不能输出“=”“==”,然后本人就卡在这个地方卡了1.5h。代码量也不大,主要是毒瘤细节模拟题。......
  • P6192 【模板】最小斯坦纳树 题解
    Description给定一个包含\(n\)个结点和\(m\)条带权边的无向连通图\(G=(V,E)\)。再给定包含\(k\)个结点的点集\(S\),选出\(G\)的子图\(G'=(V',E')\),使得:\(S\subseteqV'\);\(G'\)为连通图;\(E'\)中所有边的权值和最小。你只需要求出\(E'\)中所有边的权值......
  • BZOJ 4403序列统计题解
    缅怀zxc......
  • CSP-J 2021 初赛试题解析(第三部分:完善程序(2))
    完善程序二完整程序代码#include<iostream>usingnamespacestd;structpoint{intx,y,id;};boolequals(pointa,pointb){returna.x==b.x&&a.y==b.y;}boolcmp(pointa,pointb){returna.x!=b.x?a.x<b.x:a.y<b.y;}......
  • AT_arc170_b 的题解
    (一)因为\(a_i\)较小,那么可以对每一个\(i\),求出它右边离他最近的值为\(j\)的位置。枚举左端点和中间那个数\(a_j\),那么可以求出最小的\(k\)。这样就求出了每个左端点可以取到的最小的\(k\),记为\(b_i\)再从右到左\(b_i=\min(b_i,b_{i+1})\)。(二)AC代码。#include<bi......
  • AGC041D Problem Scores 题解
    在分值不降的条件下,要使任意一个大小为\(k\)的子集\(S\)内题目的分值之和少于任意一个大小为\(k+1\)的子集\(T\)内题目的分值之和,容易发现只需要取\(S\)为后\(k\)道题目,\(T\)为前\(k+1\)道题目时满足限制即可。换而言之,只需要对满足\(a\)的每一段长为\(k+......
  • P7075 [CSP-S2020] 儒略日 题解
    P7075[CSP-S2020]儒略日题面:题目描述为了简便计算,天文学家们使用儒略日(Julianday)来表达时间。所谓儒略日,其定义为从公元前4713年1月1日正午12点到此后某一时刻间所经过的天数,不满一天者用小数表达。若利用这一天文学历法,则每一个时刻都将被均匀的映射到数轴......
  • AT_ddcc2017_final_d なめらかな木 题解
    首先考虑暴力DP,设\(f(i,v_1,v_2,now)\)为已经将前\(i\)个数填入,\(i\)填在\(v_1\),\(j\)填在\(v_2\)点,已经填完点的状态是\(now\)(状压一下存在一个longlong里)的方案数。转移时直接枚举下一个点暴力转移,只需要保证新的点没有被访问过,并且填上新点后,\(v_1\)的所有邻接......