原文链接 https://www.cnblogs.com/cly-none/p/CSA72G.html

题意:有一个\(n \times n\)的矩阵\(A\),\(m\)次操作,每次在\(A\)上三角部分的一个子矩形中加上一个数。最后构造\(n\)个点的图\(G\),且对于所有\(i,j \ (i < j)\),边\((i,j)\)的边权为\(A_{i,j}\)。求图\(G\)的最小生成树的边权和。

\(n,m \leq 10^5\)

先把上三角矩阵补成邻接矩阵。这样每次操作就是加两个邻接矩阵的子矩形。

这种题目通常要对经典算法进行拓展。常用的最小生成树算法有Prim和Kruskal,然而在尝试之后我们发现,由于边权种类太多,Prim不行;同样Kruskal也难以提出排序后的边权。

但还有Borůvka。这个算法要求对每个联通块找到边权最小的邻边,还要合并联通块。后者用并查集能容易实现,现在仅考虑前者。

先想一个更简单的问题:对每个结点找到边权最小的邻边。这是简单的,我们只需要扫描邻接矩阵的每一行,这样每次矩形加都变成了两个区间加。用线段树维护最小值就好了。考虑原问题。这个最小值可能和这个点在同一个联通块内,因此,非常套路地,我们就再记录与最小值不在一个联通块内的次小值就可以了。

因为上述扫描线需要执行\(O(\log n)\)次,故复杂度为\(O(n \log^2 n)\)。

#include <bits/stdc++.h>
#define data DATA
using namespace std;
#define gc() getchar()
template <typename tp>
inline void read(tp& x) {
x = 0; char tmp; bool key = 0;
for (tmp = gc() ; !isdigit(tmp) ; tmp = gc())
key = (tmp == '-');
for ( ; isdigit(tmp) ; tmp = gc())
x = (x << 3) + (x << 1) + (tmp ^ '0');
if (key) x = -x;
}
typedef long long ll;
const int N = 100010;
const ll INF = 1ll << 60;
int n,m,uni[N],cnt;
ll ans;
int getfa(int pos) {
return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
struct data {
int p,l,r,v;
bool operator < (const data& a) const {
return p < a.p;
}
} dat[N << 2];
typedef pair<ll,int> pli;
#define fi first
#define se second
struct node {
pli mn[2];
ll tag;
node() {
tag = 0;
mn[0] = mn[1] = pli(INF,0);
}
} t[N << 2];
void puttag(int x,ll v) {
t[x].mn[0].fi += v;
t[x].mn[1].fi += v;
t[x].tag += v;
}
void push_down(int x) {
puttag(x<<1,t[x].tag);
puttag(x<<1|1,t[x].tag);
t[x].tag = 0;
}
void push_up(node& x,node ls,node rs) {
x.mn[1].fi = INF;
if (ls.mn[0] < rs.mn[0]) {
x.mn[0] = ls.mn[0];
if (rs.mn[0].se != x.mn[0].se)
x.mn[1] = rs.mn[0];
} else {
x.mn[0] = rs.mn[0];
if (ls.mn[0].se != x.mn[0].se)
x.mn[1] = ls.mn[0];
}
if (ls.mn[1].se != x.mn[0].se)
x.mn[1] = min(x.mn[1], ls.mn[1]);
if (rs.mn[1].se != x.mn[0].se)
x.mn[1] = min(x.mn[1], rs.mn[1]);
}
void modify(int l,int r,ll v,int x=1,int lp=1,int rp=n) {
if (lp > r || l > rp) return;
if (lp >= l && rp <= r)
return (void) puttag(x,v);
push_down(x);
int mid = (lp + rp) >> 1;
modify(l,r,v,x<<1,lp,mid);
modify(l,r,v,x<<1|1,mid+1,rp);
push_up(t[x],t[x<<1],t[x<<1|1]);
}
void build(int x=1,int lp=1,int rp=n) {
t[x].tag = 0;
if (lp == rp) {
t[x].mn[0] = pli(0ll,uni[lp]);
t[x].mn[1] = pli(INF,0);
return;
}
int mid = (lp + rp) >> 1;
build(x<<1,lp,mid);
build(x<<1|1,mid+1,rp);
push_up(t[x], t[x<<1], t[x<<1|1]);
}
pli nex[N];
void solve() {
build();
for (int i = 1 ; i <= n ; ++ i)
nex[i] = pli(INF,0);
for (int i = 1, j = 1 ; i <= n ; ++ i) {
while (j <= cnt && dat[j].p <= i)
modify(dat[j].l, dat[j].r, dat[j].v), ++ j;
node tmp = t[1];
if (tmp.mn[0].se != uni[i])
nex[uni[i]] = min(nex[uni[i]], tmp.mn[0]);
else nex[uni[i]] = min(nex[uni[i]], tmp.mn[1]);
}
for (int i = 1, j ; i <= n ; ++ i) {
j = getfa(i);
if (nex[j].se == INF) continue;
if (getfa(nex[j].se) != j) {
ans += nex[j].fi;
uni[j] = getfa(nex[j].se);
}
}
}
bool check() {
for (int i = 1 ; i <= n ; ++ i)
uni[i] = getfa(i);
for (int i = 2 ; i <= n ; ++ i)
if (uni[i] != uni[i-1]) return 1;
return 0;
}
signed main() {
read(n), read(m);
for (int i = 1, a, b, c, d, e ; i <= m ; ++ i) {
read(a), read(b), read(c), read(d), read(e);
dat[++cnt] = (data) {a, c, d, e};
dat[++cnt] = (data) {b+1, c, d, -e};
dat[++cnt] = (data) {c, a, b, e};
dat[++cnt] = (data) {d+1, a, b, -e};
}
for (int i = 1 ; i <= n ; ++ i)
uni[i] = i;
sort(dat+1,dat+cnt+1);
while (check())
solve();
cout << ans << endl;
return 0;
}

小结:本题做法乍一看是几个套路的综合,但并不简单。还是要求清晰的思维,以及熟练掌握基础算法和技巧。

【做题】CSA72G - MST and Rectangles——Borůvka&线段树的更多相关文章

  1. 【CSA72G】【XSY3316】rectangle 线段树 最小生成树

    题目大意 有一个 \(n\times n\) 的矩阵 \(A\).最开始 \(A\) 中每个元素的值都为 \(0\). 有 \(m\) 次操作,每次给你 \(x_1,x_2,y_1,y_2,w\),对 ...

  2. 刷题总结——二逼平衡树(bzoj3224线段树套splay)

    题目: Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在 ...

  3. GSS4 - Can you answer these queries IV || luogu4145上帝造题的七分钟2 / 花神游历各国 (线段树)

    GSS4 - Can you answer these queries IV || luogu4145上帝造题的七分钟2 / 花神游历各国 GSS4 - Can you answer these qu ...

  4. Codeforces 1396D - Rainbow Rectangles(扫描线+线段树)

    Codeforces 题面传送门 & 洛谷题面传送门 一道鸽了整整一年的题目,上一次提交好像是 2020 年 9 月 13 日来着的(?) 乍一看以为第 2 个提交和第 3 个提交只差了 43 ...

  5. Gym - 101982F Rectangles (扫描线+线段树)

    链接:http://codeforces.com/gym/101982/attachments 思路: 问被覆盖次数为奇数次的矩阵的面积并 扫描线求矩阵面积并我们是上界赋为-1,下界赋为1,因为要求覆 ...

  6. day1 晚上 P4145 上帝造题的七分钟2 / 花神游历各国 线段树

    #include<iostream> #include<cstdio> #include<cmath> using namespace std; ; struct ...

  7. [日记&做题记录]-Noip2016提高组复赛 倒数十天

    写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...

  8. NOIP2016考前做题(口胡)记录

    NOIP以前可能会持续更新 写在前面 NOIP好像马上就要到了,感觉在校内训练里面经常被虐有一种要滚粗的感觉(雾.不管是普及组还是提高组,我都参加了好几年了,结果一个省一都没有,今年如果还没有的话感觉 ...

  9. zoj-1610线段树刷题

    title: zoj-1610线段树刷题 date: 2018-10-16 16:49:47 tags: acm 刷题 categories: ACM-线段树 概述 这道题是一道简单的线段树区间染色问 ...

随机推荐

  1. 《Redis 命令操作》

    一:Redis 的启动与关闭 - 关闭指定端口的 Redis - redis-cli -p 9200 shutdown - 开启 Redis - redis-server redis.config 二 ...

  2. Python_tkinter(2)_常用控件

    1.Label--标签(文字/位图)控件 from tkinter import * root = Tk() root.geometry('200x200') # Label控件 字体.边框.背景 l ...

  3. nodejs技术升阶思路

    经历一两年的node后端开发,熟悉了node原生api.常用包.数据库操作语句.一两个框架等,技术会达到一个瓶颈.如果没有足够的动力去深入,很容易老死在这个瓶颈上. 下面总结一下,提升点和方向: 站在 ...

  4. 反射的应用,jdbc封装

    实现在Java中查询数据库并保存在Java中 1.创建Dept类(要查找的类) package cn.ljs; public class Dept { private int deptno; priv ...

  5. vue 父组件给子组件传值,子组件给父组件传值

    父组件如何给子组件传值 使用props 举个例子: 子组件:fromTest.vue,父组件 app.vue fromTest.vue <template> <h2>{{tit ...

  6. 数电基础之《OC门》

    OC门,又称集电极开路门,Open Collector.   为什么引入OC门?实际使用中,有时需要两个或两个以上与非门的输出端连接在同一条导线上,将这些与非门上的数据(状态电平)用同一条导线输送出去 ...

  7. 20190429 照片里面的GPS信息确实会暴露经纬度

    这是我用Android手机拍摄的照片,并上传了原图(当然在没开启定位的工作的话,照片也没有GPS这个属性显示) 2. 之前也有一种关于给陌生人点赞,通过点赞来查看你与这个陌生人的距离,我也测试了一下有 ...

  8. KubeCon + CloudNativeCon论坛2019上海

    2019年,KubeCon + CloudNativeCon和Open Source Summit在将在中国共同举办一场活动. In 2019, KubeCon + CloudNativeCon an ...

  9. Cocos Creator LabelAtlas(艺术数字的使用)

    # 艺术数字资源 (LabelAtlas) **艺术数字资源** 是一种用户自定义的资源,它可以用来配置艺术数字字体的属性. ## 创建艺术数字资源 在 **资源管理器** 中右键,可以在如下菜单中找 ...

  10. BigDecimal源码

    1 public BigDecimal(char[] in, int offset, int len, MathContext mc) {// 使用字符数组的构造方法,一般我们推荐使用的是一Strin ...