AT cf17 final J Tree MST

考场上想出的黑题,然而写挂了……

思路

考场推出 boruvka 算法,会的直接跳过就好。

结论:一个点向另外一个点连出的最小边,一定在最小生成树上。

证明:参考 Kruskal 生成树的流程,若当前边(最小边)不在最小生成树上,表明边的两端已经在同一个连通块中。那么存在一条更小的边连接当前边的两端,又已知当前边是最小的连接两个点的边,此处产生了矛盾,故可以证明上述结论。

发现把连通块看做一个点,代入上述结论依然成立,故可得到:一个连通块向另外一个连通块连出的最小边,一定在最小生成树上。

由于一条最小边可以消除两个连通块,那么一次最少可以消除 \(\frac{t}{2}\) 个连通块(这里的 \(t\))是连通块个数,易得最多进行 \(\log n\)​ 次每个连通块找最小边的操作,该图可以形成一棵树。

对于一般图,每次遍历 \(m\) 条边,对于每个连通块求最近连通块即可。

复杂度为:\(O(m\log n)\)。

一般适用于点数较少,边数较多,而又可以快速求出一个点最近的不在同一个连通块内的点的情况(即可以替代遍历 \(m\) 条边,求出连通块最近连通块的做法)。

接着我们考虑怎样快速的求出一个点最近的不在同一个连通块内的点。

这里的最近是在图上的最近,且不包括出发点本身的 \(a\) 值。

下文所述的路径长度也为图上的路径长度,且不包括出发点本身的 \(a\) 值。

考场手玩一下数据发现下述规律:

首先生成一棵树。

从 \(st\) 出发的最短路径为 \(st\to 12\),长度即为下图红色线段所标的边的边权和加 \(a_{12}\)。

此时若某个点想要寻找最近点,若 \(st\) 在这个点最近点的路径上,那么最近点不是 \(st\) 就是 \(12\)。

证明一下,因为 \(st\) 在这个点的路径上,那么可以看做是 \(st\) 到这个点的距离加上 \(st\) 到最近的一个点的距离,所以成立。

然后你就会发现,把每一次的 \(st\) 搬到点分树上,第一次是点分树的根跑一个最近点,第二次是点分树的根的儿子跑最近点……,由于不需要经过已经求过最进点的点,所以点分树上每一层只会遍历 \(n\) 个节点。

如果需要经过已经求过最近点的点可以 \(O(1)\) 出答案。

每次使用并查集判断一个点是否在同一个连通块内。如果是已经求过最近点的点(点分树上祖先),自己和其在同一个连通块那么就取其的最近点,如果不在那么就取其到其连通块内的最近点(这个肯定更优,因为第一次筛出的无限制的最近点肯定是最小的),当然也可以取其本身。

求出后直接跑 boruvka 就行。

复杂度 \(O(n\log^2n)\),常数有一点点大。

CODE

#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std; #define ll long long
#define pli pair<ll,int>
#define fi first
#define se second const int maxn=2e5+1; struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt,w;}edge[maxn<<1];
inline void add(int x,int y,int z)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
edge[tot].w=z;
head[x]=tot;
}
}T; int n,cnt;
int f[maxn],a[maxn]; ll ans; pli val[maxn],use[maxn]; struct node{int u,v;ll w;}E[maxn]; int rt;
int siz[maxn];
bool book[maxn],cut[maxn];
vector<int>son[maxn]; char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
int x=0,f=1;
char ch=nc();
while(ch<48||ch>57)
{
if(ch=='-')
f=-1;
ch=nc();
}
while(ch>=48&&ch<=57)
x=x*10+ch-48,ch=nc();
return x*f;
}
inline void W(ll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) W(x/10);
putchar(x%10+'0');
}
inline pli Min(pli a,pli b)
{
if(a.fi<b.fi) return a;
else if(a.fi==b.fi&&a.se<b.se) return a;
return b;
} inline int fr(int u){return f[u]==u?u:f[u]=fr(f[u]);}
inline void dfs_siz(int u)
{
book[u]=true;siz[u]=1;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!book[v]&&!cut[v]) dfs_siz(v),siz[u]+=siz[v];
}
book[u]=false;
}
inline int dfs_rt(int u,const int tot)
{
book[u]=true;int ret=u;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!book[v]&&!cut[v]&&(siz[v]<<1)>tot){ret=dfs_rt(v,tot);break;}
}
book[u]=false;return ret;
}
inline void build(int u,int f)
{
dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;
son[f].emplace_back(g);
for(int i=T.head[g];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!cut[v]&&!book[v]) build(v,g);
}
rt=g;
} inline void dfs2(int u,const int f,const int ff,ll dis)
{
book[u]=true;
int fu=fr(u);
if(fu!=ff) val[f]=Min(val[f],{dis+a[u],u});
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(cut[v])
{
if(book[v]) continue;
int fv=fr(v);
if(fr(use[v].se)!=ff&&fv!=ff)
{
val[f]=Min({dis+T.edge[i].w+use[v].fi,use[v].se},val[f]);
}
else if(use[v].se==f&&fv!=fu) val[f]=Min({dis+T.edge[i].w+a[v],v},val[f]);
else if(fv==fu) val[f]=Min({dis+T.edge[i].w+val[v].fi,val[v].se},val[f]);
}
else if(!book[v]&&!cut[v]) dfs2(v,f,ff,dis+T.edge[i].w);
}
book[u]=false;
}
inline void dfs(int g)
{
cut[g]=true;
dfs2(g,g,fr(g),0);
if(use[g].se==0) use[g]=Min(val[g],{a[g],g});
for(auto v:son[g]) dfs(v);
}
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read(),f[i]=i;
for(int i=1;i<n;i++)
{
int x,y;ll w;
x=read(),y=read(),w=read();
T.add(x,y,w);
T.add(y,x,w);
}
build(1,0);
memset(cut,0,sizeof(cut));
E[0].w=1e18;
while(cnt<n-1)
{
dfs(rt);
for(int i=1;i<=n;i++)
{
int fv=fr(i);
if(E[fv].w>val[i].fi+a[i]) E[fv]={i,val[i].se,val[i].fi+a[i]};
}
for(int i=1;i<=n;i++)
{
int fv=fr(E[i].v),fu=fr(E[i].u);
if(E[i].w<E[0].w&&fv!=fu)
{
ans+=E[i].w;
f[fv]=fu;
cnt++;
}
E[i]=E[0];cut[i]=0,val[i]={1e18,1e9};
}
}
cerr<<clock();
W(ans);
}

删掉 \(IO\),大概 3k。

AT cf17 final J Tree MST的更多相关文章

  1. @atcoder - CODE FESTIVAL 2017 Final - J@ Tree MST

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定 N 个点,第 i 点有一个点权 Xi,再给定一棵边带权的树 ...

  2. 题解-AtCoder Code-Festival2017 Final-J Tree MST

    Problem \(\mathrm{Code~Festival~2017~Final~J}\) 题意概要:一棵 \(n\) 个节点有点权边权的树.构建一张完全图,对于任意一对点 \((x,y)\),连 ...

  3. 【AtCoder3611】Tree MST(点分治,最小生成树)

    [AtCoder3611]Tree MST(点分治,最小生成树) 题面 AtCoder 洛谷 给定一棵\(n\)个节点的树,现有有一张完全图,两点\(x,y\)之间的边长为\(w[x]+w[y]+di ...

  4. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )

    做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...

  5. 最小生成树 (Minimum Spanning Tree,MST) --- Prim算法

    本文链接:http://www.cnblogs.com/Ash-ly/p/5409904.html 普瑞姆(Prim)算法: 假设N = (V, {E})是连通网,TE是N上最小生成树边的集合,U是是 ...

  6. 最小生成树 (Minimum Spanning Tree,MST) --- Kruskal算法

    本文链接:http://www.cnblogs.com/Ash-ly/p/5409265.html 引导问题: 假设要在N个城市之间建立通信联络网,则连通N个城市只需要N - 1条线路.这时,自然会考 ...

  7. ICPC 2016 China Final J. Mr.Panda and TubeMaster【最大费用最大流】

    有一种限制下界强制选的,但是也可以不用 把每个格点拆成两个,一个连s一个连t,对于不是必选的连中间连流量1费用0边表示不选,然后黑白染色,黑点连横着白点连竖着,边权就是这条水管的权值,然后跑最大费用最 ...

  8. 【费用流】 ICPC 2016 China Final J. Mr.Panda and TubeMaster

    表示“必须选”的模型 题目大意 题目分析 一个格子有四种方式看上去很难处理.将横竖两个方向分开考虑,会发现:因为收益只与相邻格子是否连通有关,所以可以将一个格子拆成表示横竖两个方向的,互相独立的点. ...

  9. 【AT3611】Tree MST

    题目 这个题的输入首先就是一棵树,我们考虑一下点分 我们对于每一个分治重心考虑一下跨过这个分治重心的连边情况 就是把当前分治区域内所有的点向距离分治重心最近的点连边 考虑一下这个算法的正确性,如果我们 ...

  10. China Final J - Mr.Panda and TubeMaster

    和一般的管道不同 不能类似“无限之环”或者“弯弯国”的建图,因为这两个题都是某些位置必须有,或者必须没有 但是本题可以有的位置随意,不能限制某个位置要么流2,要么流0,(实际上可能流了1过去) 所以建 ...

随机推荐

  1. 最详细STL(二)deque

    deque其实也是数组,也可以动态的添加和减少元素,但是和vector不同的是,deque可以快速的在头部和尾部添加减少元素(vector只能快速的在尾部添加),然而在插入元素的时候因为头部和尾部都可 ...

  2. draw.io 使用自定义字体

    draw.io 是我最常用的作图工具.然而,draw.io 支持的字体很少,只支持少数英文字体.不过好在 draw.io 提供了自定义选项,我们可以填入自己想要使用的字体的名称,然后 draw.io ...

  3. uni-app 商场样式

    基于ColorUI-UniApp css样式开发的商城基础模 模板基础功能实现 1.首页今日推荐 点击 会商品加一 2.分类 页面 左右列表联动 3.购物车 商品加减 4.我的 订单管理  账号密码登 ...

  4. Python新手爬虫二:爬取搜狗图片(动态)

    经过上一期爬取豆瓣影评成功后,感觉爬虫还不错,于是想爬点图片来玩玩... 搜狗图片地址:https://pic.sogou.com/?from=category 先上最后成功的源码(在D盘下创建sou ...

  5. web静态和动态的区别

    一.静态web页面: 1.在静态Web程序中,客户端使用Web浏览器(IE.FireFox等)经过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Request),告诉服务器我现在 ...

  6. Angular Material 18+ 高级教程 – Custom Themes for Material Design 3 (自定义主题 Material 3)

    v18 更新重要说明 从 Angular Material v18 开始,默认使用的是 Material 3 Design (简称 M3). 而且是正式版,不再是 experimental previ ...

  7. 提升软件测试效率与灵活性:探索Mock测试的重要性

    Mock测试是测试过程中的一种方法,用于替代那些难以构造或获取的对象,通过创建虚拟对象来进行测试.所谓难以构造的对象如何理解呢? 举例来说,像HttpServletRequest这样的对象需要在具有s ...

  8. C# 裁剪PDF页面

    在处理PDF文档时,有时需要精确地裁剪页面以适应特定需求,比如去除广告.背景信息或者仅仅是为了简化文档内容.本文将介绍如何使用免费.NET控件通过C#实现裁剪PDF页面. 免费库 Free Spire ...

  9. SuperMap iManager云套件数据动态更新刷新地图与数据服务

    一.使用背景 有这么一个需求,后端也就通过SuperMap iDesktop或数据库更新了新增或更新某个数据地理信息后,云套件SuperMap iManager中的服务没有更新,无法实时查看到更新的数 ...

  10. 微信小程序点击按钮进行页面跳转

    下面是wxml代码 <button type="primary" bindtap="go">跳转到list页面</button> 下面是 ...