首页 > 其他分享 >题解 P9701【[GDCPC2023] Classic Problem】

题解 P9701【[GDCPC2023] Classic Problem】

时间:2023-10-04 21:44:06浏览次数:76  
标签:P9701 题解 ll dsu cntV buc vtx Problem find

题如其名,确实挺经典的。

我们称边权在输入中给定的边为特殊边,其它边为平凡边。称特殊边涉及到的点为特殊点,其它点为平凡点。

显然,对于连续的若干平凡点 \([l,r]\),他们内部的最优连边方式就是连成一条链,花费 \(r-l\) 的代价。我们先把这样的代价加到答案中,然后将极长连续平凡点缩成一个点,称为区间点。

我们可以按照编号从小到大的顺序为区间点和特殊点重新标号。问题转化为给定一个由区间点和特殊点构成的 \(O(m)\) 点的完全图,求它的最小生成树。

有特殊性质的点数特别多的稠密图的最小生成树问题一般考虑使用 Boruvka 算法进行求解。对于图 \(G=(V,E)\) 求最小生成树,Boruvka 算法的思路是:每一轮找到每个连通块向外连的边权最小的边,在这轮结束后把所有最小边连上,直到整个图连通为止。显然,每一轮过后连通块数至少减半,因此轮数为 \(O(\log|V|)\),复杂度为 \(O(|E|\log|V|)\)。我们可以利用图的特殊性质,加速找到最小边的过程,使得复杂度降至 \(O(|V|\log|V|)\)。

在每一轮开始时,预处理 \(pre(u),suc(u)\) 表示节点 \(u\) 前面/后面最近的不在同一连通块的点。对于区间点,它的最小边一定是它与 \(pre(u)\) 或 \(suc(u)\) 之间的。对于特殊点,首先判一遍所有跨连通块的特殊边,然后分别找到前面/后面最近的既不在同一连通块又没有特殊边相连的点,看看哪个更小即可。可以证明,每轮的时间复杂度为 \(O(|V|)\),总复杂度为 \(O(|V|\log|V|)\)。由于重建的图的点数 \(|V|=O(m)\),总复杂度为 \(O(m\log m)\)。

// Problem: T368344 [GDCPC2023] L-Classic Problem
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/T368344?contestId=135929
// Memory Limit: 1 MB
// Time Limit: 8000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(ll x = (y); x <= (z); ++x)
#define per(x, y, z) for(ll x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
ll randint(ll L, ll R) {
    uniform_int_distribution<ll> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const ll N = 5e5 + 5, inf = 0x1f1f1f1f1f1f1f1fll;

ll T, n, m, U[N], V[N], W[N], buc[N], tot, cntV, val[N], pos[N], pre[N], suc[N], vis[N];
map<ll, ll> id;

struct Vertex {
    ll l, r;
    bool special;
    vector<tuple<ll, ll>> e;
}vtx[N];

struct Dsu {
    ll fa[N];
    void init(ll x) {rep(i, 1, x) fa[i] = i;}
    bool isroot(ll x) {return x == fa[x];}
    ll find(ll x) {return isroot(x) ? x : fa[x] = find(fa[x]);}
    bool same(ll x, ll y) {return find(x) == find(y);}
    bool merge(ll x, ll y) {
        if(same(x, y)) return false;
        x = find(x); y = find(y);
        fa[x] = y;
        return true;
    }
}dsu;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    for(cin >> T; T; --T) {
        cin >> n >> m;
        rep(i, 1, m) {
            cin >> U[i] >> V[i] >> W[i];
            buc[++tot] = U[i];
            buc[++tot] = V[i];
        }
        sort(buc + 1, buc + 1 + tot);
        tot = unique(buc + 1, buc + 1 + tot) - buc - 1;
        ll lst = 0;
        rep(i, 1, tot) {
            // segment
            if(buc[i] - lst + 1 >= 3) {
                ++cntV;
                vtx[cntV].l = lst + 1;
                vtx[cntV].r = buc[i] - 1;
                vtx[cntV].special = false;
            }
            // special
            ++cntV;
            vtx[cntV].l = vtx[cntV].r = buc[i];
            vtx[cntV].special = true;
            id[buc[i]] = cntV;
            lst = buc[i];
        }
        // segment
        if(n - lst + 1 >= 2) {
            ++cntV;
            vtx[cntV].l = lst + 1;
            vtx[cntV].r = n;
            vtx[cntV].special = false;
        }
        rep(i, 1, m) {
            vtx[id[U[i]]].e.emplace_back(id[V[i]], W[i]);
            vtx[id[V[i]]].e.emplace_back(id[U[i]], W[i]);
        }
        ll edges = 0, ans = 0;
        rep(u, 1, cntV) ans += vtx[u].r - vtx[u].l;
        dsu.init(cntV);
        while(edges < cntV - 1) { // MST - Boruvka algorithm
            rep(u, 1, cntV) {
                if(dsu.isroot(u)) {
                    val[u] = inf;
                    pos[u] = -1;
                }
            }
            rep(u, 1, cntV) {
                if(vtx[u].special) {
                    for(auto [v, w] : vtx[u].e) {
                        if(!dsu.same(u, v) && w < val[dsu.find(u)]) {
                            val[dsu.find(u)] = w;
                            pos[dsu.find(u)] = dsu.find(v);
                        }
                    }
                }
            }
            pre[1] = 0;
            rep(i, 2, cntV) pre[i] = dsu.same(i - 1, i) ? pre[i - 1] : i - 1;
            suc[cntV] = cntV + 1;
            per(i, cntV - 1, 1) suc[i] = dsu.same(i, i + 1) ? suc[i + 1] : i + 1;
            rep(u, 1, cntV) {
                if(vtx[u].special) for(auto [v, w] : vtx[u].e) vis[v] = 1;
                for(ll v = u; v >= 1; ) {
                    if(dsu.same(v, u)) v = pre[v];
                    else if(vis[v]) --v;
                    else {
                        if(vtx[u].l - vtx[v].r < val[dsu.find(u)]) {
                            val[dsu.find(u)] = vtx[u].l - vtx[v].r;
                            pos[dsu.find(u)] = dsu.find(v);
                        }
                        break;
                    }
                }
                for(ll v = u; v <= cntV; ) {
                    if(dsu.same(u, v)) v = suc[v];
                    else if(vis[v]) ++v;
                    else {
                        if(vtx[v].l - vtx[u].r < val[dsu.find(u)]) {
                            val[dsu.find(u)] = vtx[v].l - vtx[u].r;
                            pos[dsu.find(u)] = dsu.find(v);
                        }
                        break;
                    }
                }
                if(vtx[u].special) for(auto [v, w] : vtx[u].e) vis[v] = 0;
            }
            rep(u, 1, cntV) {
                if(dsu.isroot(u) && pos[u] != -1 && dsu.isroot(pos[u])) {
                    ++edges;
                    ans += val[dsu.find(u)];
                    dsu.merge(u, pos[u]);
                }
            }
        }
        cout << ans << endl;
        rep(i, 1, tot) buc[i] = id[i] = 0;
        rep(i, 1, cntV) {
            vis[i] = vtx[i].l = vtx[i].r = 0;
            vtx[i].special = false;
            vtx[i].e.clear();
        }
        tot = cntV = 0;
    }
    return 0;
}

标签:P9701,题解,ll,dsu,cntV,buc,vtx,Problem,find
From: https://www.cnblogs.com/ruierqwq/p/LG-P9701.html

相关文章

  • [SHOI2009] 会场预约 题解
    LG任意时刻每个点最多被一条线段覆盖暴力删每条线段是对的插入\([l,r]\)时需要删除的线段要么被\([l,r]\)包含,要么覆盖\(l\)或\(r\)性质非常强所以做法非常多一种比较神奇的:对于两条线段\([l_{1},r_{1}],[l_{2},r_{2}]\),定义<为\(r_{1}<l_{2}\),即线段\(1\)完......
  • 题解 accoders::NOI 5511【漂亮轰炸(bomb)】
    题解accoders::NOI5511【漂亮轰炸(bomb)】http://47.92.197.167:5283/contest/406/problem/4BZOJ3252是弱化版。problem一棵树,边带权。\(Q\)次询问,给定\(k\)和一个首都点,选择\(k\)条路径轰炸,其中必须由一轮要轰炸首都,但没有要求每条路径都经过首都。每条边只能被炸一次,......
  • 题解 P9695【[GDCPC2023] Traveling in Cells】
    显然,询问的答案即为\(x\)所在的极长的满足颜色均在\(\mathbb{A}\)内的连续段的权值和。如果我们能维护对颜色的单点修改,以及求出某个位置所在极长连续段的左右端点\(l,r\),只需要树状数组即可求出答案。一个朴素的想法是对每种颜色开一棵线段树,单点修改是平凡的,极长连续段左......
  • 题解 P9702【[GDCPC2023] Computational Geometry】
    这题一看就不是计算几何,考虑区间DP。设凸多边形的\(n\)个顶点依次为\(P_1,P_2,\cdots,P_n\)。设\(f_{i,j}\)在\(i<j\)时表示\(P_i,P_{i+1},\cdots,P_{j-1},P_j\)组成的多边形的直径的平方,在\(i>j\)时表示\(P_i,P_{i+1},\cdots,P_n,P_1,\cdots,P_{j-1},P_j\)组......
  • 题解 P9697【[GDCPC2023] Canvas】
    好题。后面的操作会覆盖前面的操作,这东西不好处理,我们不妨时光倒流,将问题转化为一个位置一旦被填了数,就再也不会变了。如果解决了这个问题,只需将操作序列倒过来,就得到了原问题的解。显然,所有\(x_i=y_i=2\)的操作会最先被执行,所有\(x_i=y_i=1\)的操作会最后被执行。只需要给......
  • 高橋君 题解
    AtCoder天下一プログラマーコンテスト2014本戦高橋君题意给定\(n,k\),求\[\sum\limits_{i=0}^{k}\dbinom{n}{i}\]多测,\(1\len,k,T\le10^5\)。题解可以考虑使用莫队求解,下文讲述如何移动指针。\(n\rightarrown+1\)根据杨辉三角递推式\(\dbinom{n}{m}=......
  • 题解 P2674 《瞿葩的数字游戏》T2-多边形数
    题目描述给你一个正整数数\(n\),问你它是不是多边形数\(K\),如果是,设\(K_1\)是最小的\(K\),\(K_2\)是次小的\(K\),输出\(K_1\)和\(K_2\)。具体思路我们主要来看上面这张表里有什么规律。性质1:\(1\)是任何一个多边形数。性质2:\(2\)不是任何一个多边形数。性......
  • 情报破译 题解
    \(d<n\le2e5,m\le10,1\lep\le10^9,0\lea_i\le1e9\)每个位运算之间独立,所以我们可以构造一个\(\{2^{k-1},2^{k-1}.....\}\)和一个\(\{0,0,0...\}\)的数组,让他们倍增去做如上运算,最后用他们把\(p\)轮拼出来,我们就知道了\(i\)位置上二进制下第\(j\)位如果是\(0\)......
  • 国标GB28181协议下视频监控平台EasyGBS播放器起播慢或延迟高问题解决方案
    GB28181视频监控国标平台EasyGBS是基于国标GB28181协议、支持多路设备同时接入的视频监控/视频云服务平台,支持对多平台、多终端分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。国标GB28181平台EasyGBS可提供视频直播监控、云端录像、云存储、检索回放、智能告警、语音对讲、平......
  • P1025 [NOIP2001 提高组] 数的划分 题解
    题目传送门本题共有两种方法,分别是递归深搜和动态规划方法一:递归深搜Solution从小到大一一枚举每一个划分的数,。只要找到一种方案就记录,具体细节代码中有注释。Code#include<bits/stdc++.h>usingnamespacestd;intn,k,ans;voiddfs(intstart,intstep,intsum){......