题目大意

  有一个 \(n\times n\) 的矩阵 \(A\)。最开始 \(A\) 中每个元素的值都为 \(0\)。

  有 \(m\) 次操作,每次给你 \(x_1,x_2,y_1,y_2,w\),对于满足 \(x_1\leq i\leq x_2,y_1\leq j\leq y_2\) 的数对 \((i,j)\),把 \(A_{i,j}\) 的值增加 \(w\)。

  最后构造一个 \(n\) 个点的无向图 \(G\)。对于满足 \(1\leq i<j\leq n\) 的数对 \((i,j)\),在 \(G\) 中加一条连接着 \(i,j\),边权为 \(A_{i,j}\) 的边。

  求 \(G\) 的最小生成树的边权和。

  \(1\leq n,m\leq 100000,1\leq x_1\leq x_2<y_1\leq y_2\leq n,-{10}^6\leq w\leq {10}^6\);

题解

  似乎 prim 和 kruskal 算法都不太好做这道题。

  还有个算法叫 boruvka。

  大概就是每一轮对于每个连通块求出这个连通块与其他连通块间边权最小的边,然后把这两个连通块缩在一起。

  总共会缩 \(O(\log n)\) 轮。

  用扫描线+线段树处理出最小值和(连通块与最小值不同)的最小值。

  这样如果最小值和 \(i\) 在同一个连通块中,就选第二个就好了。

  时间复杂度:\(O(m\log^2n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll inf=0x3fffffffffffffffll;
typedef std::pair<pll,pll> orzzjt;
const int N=100010;
orzzjt merge(orzzjt a,orzzjt b)
{
orzzjt c;
if(a.first<b.first)
{
c=a;
if(b.first.second!=c.first.second)
c.second=min(c.second,b.first);
else
c.second=min(c.second,b.second);
}
else
{
c=b;
if(a.first.second!=c.first.second)
c.second=min(c.second,a.first);
else
c.second=min(c.second,a.second);
}
return c;
}
int c[N];
namespace seg
{
orzzjt s[4*N];
ll t[4*N];
#define mid ((L+R)>>1)
#define lc (p<<1)
#define rc ((p<<1)|1)
void mt(int p)
{
s[p]=merge(s[lc],s[rc]);
}
void build(int p,int L,int R)
{
t[p]=0;
if(L==R)
{
s[p].first=pll(0,c[L]);
s[p].second=pll(inf,0);
return;
}
build(lc,L,mid);
build(rc,mid+1,R);
mt(p);
}
void add(int p,ll v)
{
t[p]+=v;
s[p].first.first+=v;
s[p].second.first+=v;
}
void push(int p)
{
if(t[p])
{
add(lc,t[p]);
add(rc,t[p]);
t[p]=0;
}
}
void add(int p,int l,int r,ll v,int L,int R)
{
if(l<=L&&r>=R)
{
add(p,v);
return;
}
push(p);
if(l<=mid)
add(lc,l,r,v,L,mid);
if(r>mid)
add(rc,l,r,v,mid+1,R);
mt(p);
}
orzzjt query(int p,int l,int r,int L,int R)
{
if(l<=L&&r>=R)
return s[p];
push(p);
if(r<=mid)
return query(lc,l,r,L,mid);
if(l>mid)
return query(rc,l,r,mid+1,R);
return merge(query(lc,l,r,L,mid),query(rc,l,r,mid+1,R));
}
}
struct info
{
int x,y1,y2,w;
info(int a=0,int b=0,int c=0,int d=0):x(a),y1(b),y2(c),w(d){}
};
int cmp(info a,info b)
{
return a.x<b.x;
}
info a[4*N];
int n,m;
int t;
int cnt;
int f[N];
ll ans;
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int merge(int x,int y)
{
if(find(x)==find(y))
return 0;
f[find(x)]=find(y);
return 1;
}
pll h[N];
int e[N];
int main()
{
open("72G");
scanf("%d%d",&n,&m);
int x1,x2,y1,y2,w;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&w);
a[++t]=info(x1,y1,y2,w);
a[++t]=info(x2+1,y1,y2,-w);
a[++t]=info(y1,x1,x2,w);
a[++t]=info(y2+1,x1,x2,-w);
}
sort(a+1,a+t+1,cmp);
cnt=n;
for(int i=1;i<=n;i++)
{
f[i]=i;
c[i]=i;
e[i]=i;
}
while(cnt>1)
{
seg::build(1,1,n);
int j=1;
for(int i=1;i<=cnt;i++)
h[i]=pll(inf,inf);
for(int i=1;i<=n;i++)
{
for(;j<=t&&a[j].x==i;j++)
seg::add(1,a[j].y1,a[j].y2,a[j].w,1,n);
orzzjt s;
if(i==1)
s=seg::query(1,i+1,n,1,n);
else if(i==n)
s=seg::query(1,1,i-1,1,n);
else
s=merge(seg::query(1,1,i-1,1,n),seg::query(1,i+1,n,1,n));
if(s.first.second!=c[i])
h[c[i]]=min(h[c[i]],s.first);
else
h[c[i]]=min(h[c[i]],s.second);
}
for(;j<=t;j++)
seg::add(1,a[j].y1,a[j].y2,a[j].w,1,n);
for(int i=1;i<=cnt;i++)
if(merge(e[i],e[h[i].second]))
ans+=h[i].first;
cnt=0;
for(int i=1;i<=n;i++)
if(find(i)==i)
{
c[i]=++cnt;
e[cnt]=i;
}
for(int i=1;i<=n;i++)
c[i]=c[find(i)];
}
printf("%lld\n",ans);
return 0;
}

【CSA72G】【XSY3316】rectangle 线段树 最小生成树的更多相关文章

  1. 【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树

    题面 在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci. Byteasar作为Byteland 公路建设项目的总工程师,他决定选定 ...

  2. 【做题】CSA72G - MST and Rectangles——Borůvka&线段树

    原文链接 https://www.cnblogs.com/cly-none/p/CSA72G.html 题意:有一个\(n \times n\)的矩阵\(A\),\(m\)次操作,每次在\(A\)上三 ...

  3. 【省选十连测之一】【线段树】【最小生成树之Kruskal】公路建设

    目录 题意 输入格式 输出格式 数据范围 思路 代码 题意 有n个点,m条双向道路,其中第条公路的两个端点是u[i],v[i],费用是c[i]. 现在给出q个询问,每次给定一个L和一个R,要求你只能够 ...

  4. 2018.10.26 NOIP模拟 图(最小生成树+线段树合并)

    传送门 首先最开始说的那个一条路径的权值就是想告诉你两个点之间的贡献就是瓶颈边的权值. 那么肯定要用最小生成树算法. 于是我考场上想了30min+30min+30min+的树形dpdpdp 发现转移是 ...

  5. bzoj 5216 [Lydsy2017省队十连测]公路建设 线段树维护 最小生成树

    [Lydsy2017省队十连测]公路建设 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 93  Solved: 53[Submit][Status][ ...

  6. 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并

    题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...

  7. [BZOJ1790][AHOI2008]Rectangle 矩形藏宝地(四维偏序,CDQ+线段树)

    1790: [Ahoi2008]Rectangle 矩形藏宝地 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 553  Solved: 193[Subm ...

  8. 【bzoj2238】Mst 最小生成树+树链剖分+线段树

    题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...

  9. 【Codeforces827D/CF827D】Best Edge Weight(最小生成树性质+倍增/树链剖分+线段树)

    题目 Codeforces827D 分析 倍增神题--(感谢T*C神犇给我讲qwq) 这道题需要考虑最小生成树的性质.首先随便求出一棵最小生成树,把树边和非树边分开处理. 首先,对于非树边\((u,v ...

随机推荐

  1. Flutter map 妙用及 .. 使用

    前言 本篇文章对于熟悉 flutter 或者 dart 的小伙伴来说可能觉得比较简单,但是对于初学者或者没用过的小伙伴还是有些收获的. 背景 说到 map 妙用的发现,还要归功于 Tooltip 的研 ...

  2. 表单数据验证方法(二)——ASP.NET后台验证

    昨天写了一下关于如何在前台快捷实现表单数据验证的方法,今天接着昨天的,把后台实现数据验证的方法记录一下.先说明一下哈,我用的是asp.net,所以后台验证方法也是基于.net mvc来做的. 好了,闲 ...

  3. Centos7 Jenkins日志过大

    df 查看 占用 [root@instance-ncwnnt0e /]# df Filesystem 1K-blocks Used Available Use% Mounted on devtmpfs ...

  4. 制作联动时,数据绑定combox控件会触发SelectedIndexChanged事件

    看过很多个网站的解决办法,基本雷同,还不能解决,真怀疑他们是互相直接炒的,没事通过验证. 在做省市区的三级联动时候出现这个问题,最后通过先设置值对象和显示对象,最后才绑定数据,这样一个逻辑操作,什么问 ...

  5. .NET redis cluster

    一.下载Windows版本Redis 下载链接:https://github.com/MSOpenTech/redis/releases(根据系统选择对应版本) 二.修改默认的配置文件 如上图两个配置 ...

  6. OO第二次博客作业——电梯调度

    OO第二次博客作业——电梯调度 前言 最近三周,OO课程进入多线程学习阶段,主要通过三次电梯调度作业来学习.从单部电梯的傻瓜式调度到有性能要求的调度到多部电梯的调度,难度逐渐提升,对同学们的要求逐渐变 ...

  7. jquery之冒泡事件介绍以及阻止冒泡

    什么是事件冒泡 <div style="width: 200px;height: 200px;background: red;margin: 200px auto;" onc ...

  8. 微信小程序 canvas 文字居中

    drawCanvas: function(ctx) { //... // 昵称 ctx.setFontSize(16) //字体大小 ctx.setFillStyle('#fff') //字体颜色 c ...

  9. Hacking HackDay: Albania

    概述: Name: HackDay: Albania Date release: 18 Nov 2016 Author: R-73eN Series: HackDay 下载: https://down ...

  10. 华途软件受控XML转EXCEL

    公司加密系统用的是华途的产品.最近公司高层想要重新梳理公司信息安全管理情况,华途加密系统的梳理和优化是重中之重. 今天公司领导要求IT导出目前系统中所有软件.后缀的受控情况,然后IT吭哧吭哧地把华途软 ...