首页 > 其他分享 >Daleks' Invasion 题解

Daleks' Invasion 题解

时间:2023-11-06 19:22:17浏览次数:69  
标签:Daleks int 题解 top 最小 生成 dep dfn Invasion

Daleks' Invasion

题目大意

给定一张无向带权图,对于每条边求一个最大的 \(x\),使得将这条边的边权修改为 \(x\) 后这条边能位于这张图的最小生成树上。

思路分析

没事干了就把之前写过的题拉出来水题解。

我们先把原图的最小生成树求出来,考虑每条边 \((u,v)\),分类讨论:

  • 如果这条边不在原图最小生成树上:

容易发现,这时这条边的 \(x\) 就是在最小生成树上 \(u\) 到 \(v\) 的最大边权 \(w\)。用一个随便什么东西维护就行了。(倍增,树剖套 ST 表,树剖套线段树……能用的东西太多了)

证明是容易的:

首先考虑可行性,当我们将这条边的边权修改为 \(w\) 后,我们显然可以通过断掉之前最大边权所在的边,再加入这条边的方式生成一个新的合法最小生成树。

再考虑极大性,这就更显然了,由于最小生成树是一颗瓶颈生成树,也就是说最小生成树保证最大边权最小。那么如果 \(x=w+1\),并且这条边还在最小生成树上的话,那么就与最小生成树的性质矛盾了,因此极大性成立。

  • 如果这条边在原图的最小生成树上:

类似的,我们可以发现这条边的 \(x\) 是在原图中 \(u\) 到 \(v\) 的所有不经过树边的简单路径中的最小边权 \(w\)。

证明也类似上文的证明,此处略去。读者自证不难。

实现方式也很多,可以先枚举所有不在最小生成树上的边 \((u,v,w)\),用树剖套线段树支持 \(u\) 到 \(v\) 的链对 \(w\) 取 \(\min\)。

也可以先排个序然后将区间取 \(\min\) 改为区间赋值,但其实没什么区别。

那么这题就做完了,总时间复杂度为 \(O(m\log ^2n)\)。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>

using namespace std;
const int N = 1001000;
#define inf 1000000000
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)

int n, m, in1, in2, in3, cnt;
int fa[N], siz[N], son[N], dep[N], top[N], dfn[N], rnk[N];
int vis[N], fat[N], pw[N], ans[N], inp[N];

int find(int x){
    return fat[x] == x ? x : fat[x] = find(fat[x]);
}

struct Edge{
    int u, v, w, id;
}e[N];

vector <pair<int, int>> to[N];

void dfs_1(int s, int gr){
    dep[s] = dep[gr] + 1;
    siz[s] = 1; fa[s] = gr;
    for (auto [v, w] : to[s]) {
        if (v == gr) continue;
        dfs_1(v, s);
        siz[s] += siz[v];
        pw[v] = w;
        if (siz[son[s]] < siz[v]) son[s] = v;
    }
}

void dfs_2(int s, int tp){
    top[s] = tp; dfn[s] = ++ cnt; rnk[cnt] = s;
    if (!son[s]) return ;
    dfs_2(son[s], tp);
    for (auto [v, w] : to[s]) 
        if (v != fa[s] && v != son[s]) dfs_2(v, v);
}

struct STn{
    int maxn;
    int tag;
};
struct ST{
    STn a[N << 2];
    void build(int p, int l, int r){
        a[p].tag = inf;
        if (l == r) return a[p].maxn = inp[l], void();
        build(ls, l, mid); build(rs, mid + 1, r);
        a[p].maxn = max(a[ls].maxn, a[rs].maxn);
    }
    void min_t(int p, int k){
        a[p].maxn = min(a[p].maxn, k);
        a[p].tag = min(a[p].tag, k);
    }
    void push_down(int p){
        if (a[p].tag == inf) return ;
        min_t(ls, a[p].tag); min_t(rs, a[p].tag);
        a[p].tag = inf;
    }
    void change(int p, int l, int r, int x, int y, int k){
        if (x <= l && r <= y) return min_t(p, k);
        push_down(p);
        if (x <= mid) change(ls, l, mid, x, y, k);
        if (y > mid) change(rs, mid + 1, r, x, y, k);
        a[p].maxn = max(a[ls].maxn, a[rs].maxn);
    }
    int ask(int p, int l, int r, int x, int y){
        if (x <= l && r <= y) return a[p].maxn;
        push_down(p);
        if (y <= mid) return ask(ls, l, mid, x, y);
        if (x > mid) return ask(rs, mid + 1, r, x, y);
        return max(ask(ls, l, mid, x, y), ask(rs, mid + 1, r, x, y));
    }
}tree;

int ask(int x, int y){
    int res = -inf;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        res = max(res, tree.ask(1, 1, n, dfn[top[x]], dfn[x]));
        x = fa[top[x]]; 
    }
    if (x != y) res = max(res, tree.ask(1, 1, n, min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y])));
    return res;
}

void change(int x, int y, int k){
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        tree.change(1, 1, n, dfn[top[x]], dfn[x], k);
        x = fa[top[x]];
    }
    if (x != y) tree.change(1, 1, n, min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), k);
}

int main(){
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i ++) fat[i] = i;
    for (int i = 1; i <= m; i ++) {
        scanf("%d %d %d", &in1, &in2, &in3);
        e[i] = Edge{in1, in2, in3, i};
    }
    sort(e + 1, e + m + 1, [](Edge a, Edge b){return a.w < b.w;});
    for (int i = 1; i <= m; i ++)
        if (find(e[i].u) != find(e[i].v)) {
            to[e[i].u].push_back({e[i].v, e[i].w});
            to[e[i].v].push_back({e[i].u, e[i].w});
            vis[e[i].id] = 1;
            fat[find(e[i].u)] = find(e[i].v);
        }
    dfs_1(1, 0); dfs_2(1, 1);
    for (int i = 1; i <= n; i ++) inp[dfn[i]] = pw[i];
    tree.build(1, 1, n);
    for (int i = 1; i <= m; i ++)
        if (!vis[e[i].id]) ans[e[i].id] = ask(e[i].u, e[i].v);
    for (int i = 1; i <= n; i ++) inp[i] = inf;
    tree.build(1, 1, n);
    for (int i = 1; i <= m; i ++)
        if (!vis[e[i].id]) change(e[i].u, e[i].v, e[i].w);
    for (int i = 1; i <= m; i ++) 
        if (vis[e[i].id]) {
            int u = e[i].u, v = e[i].v;
            if (dep[u] < dep[v]) swap(u, v);
            ans[e[i].id] = tree.ask(1, 1, n, dfn[u], dfn[u]);
        }
    for (int i = 1; i <= m; i ++) cout << ans[i] << '\n';
    return 0;
}

标签:Daleks,int,题解,top,最小,生成,dep,dfn,Invasion
From: https://www.cnblogs.com/TKXZ133/p/17813502.html

相关文章

  • Groceries in Meteor Town 题解
    GroceriesinMeteorTown题目大意维护一颗带权树,支持以下操作:将\([l,r]\)内的点变为白色。将\([l,r]\)内的点变为黑色。查询点\(x\)到任意一个白色节点的简单路径上的最大值。思路分析没事干了把以前的题拿出来写题解。看到『简单路径上的最大值』的字样首......
  • Harvester 题解
    Harvester题目大意给定\(n\timesm\)的网格,每次可以选一行或一列,将这一行或一列上的数全部取走,最多可以取四次,问取走的数的和的最大值。思路分析没事干了把以前写过的题拿出来写题解。分类讨论题。在只能取四次的情况下一共只有这么几种可能:选四行:毫无疑问,行之间互不......
  • 大文件上传 问题解决三种方案
    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。如果文件体积比较大,或者网络条件不好时,上传的时间会......
  • vue视频直接播放rtsp流;vue视频延迟问题解决;webRTC占cpu太大卡死问题解决;解决webRTC播
    vue视频直接播放rtsp流;vue视频延迟问题解决;webRTC占cpu太大卡死问题解决;解决webRTC播放卡花屏问题::https://blog.csdn.net/killerdoubie/article/details/133884070......
  • 【洛谷 P1046】[NOIP2005 普及组] 陶陶摘苹果 题解(比较)
    [NOIP2005普及组]陶陶摘苹果题目描述陶陶家的院子里有一棵苹果树,每到秋天树上就会结出个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。现在已知个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的......
  • Linux下内存buff/cache占用过多问题解决
    在Linux下经常会遇到buff/cache内存占用过多问题,如果buff/cache占用过大的,free空闲内存就很少,影响使用;通常内存关系是:普通机器:total=used+free虚拟机器:total=used+free+buff/cache这个时候可以看到buff/cache占用的内存非常大,这个时候可以使用一下命令去清除一下cache内存echo1>......
  • 题解 P6880 [JOI 2020 Final] オリンピックバス
    洛谷。题意应该显然,注意最多只能翻转一条边,并且可以不翻转。分析首先观察数据范围\(2\leN\le200\),\(1\leM\le5\times10^4\),可以发现我们的\(N\)和\(M\)并不是同级的,因此,在众多的最短路算法中,我们应当选择不加堆优化的dijkstra算法,并且使用邻接矩阵,这是\(O(n^2)......
  • 【题解】NOIP2021 - 方差
    NOIP2021-方差https://www.luogu.com.cn/problem/P7962想当年我第一次站在noip赛场上,过了T1剩下三题就一题不会了……幸好这题拿了点分水了个一等。观察操作:若对于连续的三个数\(a,b,c\),对\(b\)进行一次操作后就变成了\(a,a+c-b,c\)。求出两个数组的差分数组:\(b-a,c......
  • 2023联合省选 题解
    目录D1T1P9166[省选联考2023]火车站D1T2P9167[省选联考2023]城市建造D1T3P9168[省选联考2023]人员调度D2T1P9169[省选联考2023]过河卒D2T2P9170[省选联考2023]填数游戏D2T3P9171[省选联考2023]染色数组D1T1P9166[省选联考2023]火车站性质很好找。关......
  • 题解 P6878 [JOI 2020 Final] JJOOII 2
    好久没写题解,水一篇。题意题意显然。分析看到这道题,我们就应该进行一个小贪心,对于最左边某一字符,直到最右边的这一字符,我们不会在中间删除同样的字符,不然则可以保留这一字符,将两边往内缩。也就是说,我们确定了最左边的J后,那么留下最后一个J必然是当前这个J的后面的第\(......