Problem

\(\mathrm{Code~Festival~2017~Final~J}\)

题意概要:一棵 \(n\) 个节点有点权边权的树。构建一张完全图,对于任意一对点 \((x,y)\),连一条长度为 \(w[x] + w[y]+ dis(x, y)\) 的边。求这张图的最小生成树。

\(n\leq 2\times 10^5\)

Solution

在操场上晒太阳时想到的做法,求 \(\mathrm{MST}\) 可以使用另一种贪心算法:每次找到每个点连出去的最短的边,并将其合并,一次是 \(O(n)\),由于每次点数至少减半,所以总共不超过 \(\log n\)次,总复杂度 \(O(n\log n)\)

使用这种贪心算法后,只需每次找到离每个点最近的点。

可以使用点分治,设已经合并的点为同一连通块。考虑分治中心为 \(x\),只考虑过分治中心的路径,求出 \(dep+w\) 最小的点,对于每棵子树内的点,只有非子树内的点可能做贡献,而对于每个点,只有非同连通块的点可做贡献。所以需要维护四个值,这样较麻烦,或者是只维护两个值加上处理前后缀(具体可以看代码)。复杂度 \(O(n\log^2n)\)

然后搜了一波题解,发现一群人在同一天使用了同一个做法(可能是他们在讲课后统一发的题解):同样考虑上述贪心,只是点分治时不用考虑是否在同一子树内,而是都连过去,这样保证结果不会低于答案,稍加分析发现能得到最优解。

又看了看官方正解,发现不需要点分治,直接换根Dp即可……可能是老年选手已经开始老年痴呆了

Code

哦,这样常数有点大,我的代码跑极限数据 \(\mathrm {5.01s}\),会 T 三个点,预处理点分树即可

//Code Festival 2017 Final-J
#include <bits/stdc++.h>
using namespace std;
typedef long long ll; template <typename _tp> inline void cmax(_tp&A,const _tp&B){if(A < B) A = B;}
template <typename _tp> inline void cmin(_tp&A,const _tp&B){if(A > B) A = B;} template <typename _tp> inline void read(_tp&x){
char c11=getchar(),ob=0;x=0;
while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
} const ll Inf = 2e18;
const int N = 201000;
struct Edge{int v,nxt;ll w;}a[N+N+N];
int head[N],Head[N],vs[N],w[N],id[N];
int n,_; inline void ad(){
static int x,y,z; read(x), read(y), read(z);
a[++_].v = y, a[_].w = z, a[_].nxt = head[x], head[x] = _;
a[++_].v = x, a[_].w = z, a[_].nxt = head[y], head[y] = _;
} namespace dsu{
int dad[N];
int find(int x){return dad[x]? dad[x] = find(dad[x]): x;}
bool check(int x,int y){return find(x) == find(y);}
bool merge(int x,int y){
static int p1,p2;
if((p1 = find(x)) == (p2 = find(y))) return false;
dad[p1] = p2; return true;
}
} namespace TD{
int sz[N], rt, Mi, nn;
void get_rt(int x,int las){
sz[x] = 1;int mx = 0;
for(int i=head[x];i;i=a[i].nxt)
if(a[i].v!=las and !vs[a[i].v]){
get_rt(a[i].v,x);
sz[x] += sz[a[i].v];
cmax(mx, sz[a[i].v]);
}
cmax(mx, nn - sz[x]);
if(mx < Mi) Mi = mx, rt = x;
} void Get_rt(int x,int xn){rt = 0, nn = xn, Mi = 2e9; get_rt(x,0);} void build(int x,int las){
vs[x] = 1;
a[++_].v = x, a[_].nxt = Head[las], Head[las] = _;
get_rt(x,0);
for(int i=head[x];i;i=a[i].nxt)
if(!vs[a[i].v]){
Get_rt(a[i].v,sz[a[i].v]);
build(rt,x);
}
}
} struct node{
ll v;int id;
inline node(){}
inline node(const ll&V,const int&Id):v(V),id(Id){}
}tr[N], p[N], Mx, Mi, Fir[N], Sec[N]; node pre_fir[N], pre_sec[N];
node suf_fir[N], suf_sec[N]; inline void upd(node&A, node&B, node nw){
if(nw.v < A.v) {
if(nw.id == A.id) {A = nw; return ;}
B = A, A = nw; return ;
}
if(nw.v < B.v)
if(nw.id != A.id) B = nw;
} void get_val(int x,int las,ll dep){
upd(Mi, Mx, node(dep+w[x],id[x]));
for(int i=head[x];i;i=a[i].nxt)
if(a[i].v!=las and !vs[a[i].v])
get_val(a[i].v,x,dep+a[i].w);
} void cover(int x,int las,ll dep,node A,node B){
if(id[x] != A.id and dep + A.v < p[x].v)
p[x].v = dep + A.v, p[x].id = A.id;
if(id[x] != B.id and dep + B.v < p[x].v)
p[x].v = dep + B.v, p[x].id = B.id;
for(int i=head[x];i;i=a[i].nxt)
if(a[i].v!=las and !vs[a[i].v])
cover(a[i].v,x,dep+a[i].w,A,B);
} int to[N], to_w[N]; void work(int x){
vs[x] = 1;
int top = 0;
for(int i=head[x];i;i=a[i].nxt)
if(!vs[a[i].v]){
Mi = node(w[x],id[x]), Mx = node(Inf,0);
get_val(a[i].v,x,a[i].w);
++top, to[top] = a[i].v, to_w[top] = a[i].w;
Fir[top] = Mi, Sec[top] = Mx;
} pre_fir[1] = Fir[1];
pre_sec[1] = Sec[1];
for(int i=2;i<=top;++i){
pre_fir[i] = pre_fir[i-1];
pre_sec[i] = pre_sec[i-1];
upd(pre_fir[i], pre_sec[i], Fir[i]);
upd(pre_fir[i], pre_sec[i], Sec[i]);
}
suf_fir[top] = Fir[top];
suf_sec[top] = Sec[top];
for(int i=top-1;i>=1;--i){
suf_fir[i] = suf_fir[i+1];
suf_sec[i] = suf_sec[i+1];
upd(suf_fir[i], suf_sec[i], Fir[i]);
upd(suf_fir[i], suf_sec[i], Sec[i]);
} node A,B;
for(int i=1;i<=top;++i){
A = node(w[x],id[x]), B = node(Inf,0);
if(i!=1) upd(A,B,pre_fir[i-1]);
if(i!=1) upd(A,B,pre_sec[i-1]);
if(i!=top) upd(A,B,suf_fir[i+1]);
if(i!=top) upd(A,B,suf_sec[i+1]);
cover(to[i],x,to_w[i],A,B);
} if(top){
A = pre_fir[top], B = pre_sec[top];
if(id[x] != A.id and A.v < p[x].v)
p[x].v = A.v, p[x].id = A.id;
if(id[x] != B.id and B.v < p[x].v)
p[x].v = B.v, p[x].id = B.id;
} for(int i=Head[x];i;i=a[i].nxt)
work(a[i].v);
} int main(){
read(n);
for(int i=1;i<=n;++i)read(w[i]);
for(int i=1;i<n;++i)ad(); TD::build(1,0); int Tot = n; ll Ans = 0ll;
while(Tot > 1){
for(int i=1;i<=n;++i) id[i] = dsu::find(i), p[i].v = Inf, vs[i] = 0;
work(a[Head[0]].v);
for(int i=1;i<=n;++i) tr[i].v = Inf;
for(int i=1,t;i<=n;++i){
t = dsu::find(i);
if(tr[t].v > w[i] + p[i].v)
tr[t].v = p[i].v + w[i], tr[t].id = p[i].id;
}
for(int i=1;i<=n;++i)
if(dsu::find(i) == i){
if(dsu::check(i, tr[i].id)) continue;
Ans += tr[i].v, dsu::merge(i, tr[i].id);
--Tot;
}
}
printf("%lld\n",Ans);
return 0;
}

题解-AtCoder Code-Festival2017 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 FESTIVAL 2016 Grand Final E - Water Distribution

    Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution 题目链接:https://atcoder.jp/contests/cf16- ...

  3. CF Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined)

    1. Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) B. Batch Sort    暴力枚举,水 1.题意:n*m的数组, ...

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

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

  5. [AtCoder Code Festival 2017 QualB D/At3575] 101 to 010 - dp

    [Atcoder Code Festival 2017 QualB/At3575] 101 to 010 有一个01序列,每次可以选出一个101,使其变成010,问最优策略下能操作几次? 考虑像 11 ...

  6. Atcoder CODE FESTIVAL 2016 Final G - Zigzag MST[最小生成树]

    题意:$n$个点,$q$次建边,每次建边选定$x,y$,权值$c$,然后接着$(y,x+1,c+1),(x+1,y+1,c+2),(y+1,x+2,c+3),(x+2,y+2,c+4)\dots$(画 ...

  7. 题解【AtCoder - CODE FESTIVAL 2017 qual B - D - 101 to 010】

    题目:https://atcoder.jp/contests/code-festival-2017-qualb/tasks/code_festival_2017_qualb_d 题意:给一个 01 串 ...

  8. 【题解】Popping Balls AtCoder Code Festival 2017 qual B E 组合计数

    蒟蒻__stdcall终于更新博客辣~ 一下午+一晚上=一道计数题QAQ 为什么计数题都这么玄学啊QAQ Prelude 题目链接:这里是传送门= ̄ω ̄= 下面我将分几个步骤讲一下这个题的做法,大家不 ...

  9. AtCoder Code Festival 2017 Team Relay J - Indifferent

    题目大意:共$2n$个价格$p_i$.两人轮流取.你每次取最大的,对方每次随机取.问你取的期望和是多少. 题解:从小到大排序,$\sum\limits_{i=0}^{2n-1} \frac{i*p_i ...

随机推荐

  1. Python爬虫 爬取百合网的女人们和男人们

    学Python也有段时间了,目前学到了Python的类.个人感觉Python的类不应称之为类,而应称之为数据类型,只是数据类型而已!只是数据类型而已!只是数据类型而已!重要的事情说三篇. 据书上说一个 ...

  2. Linux下修改MySQL数据表中字段属性

    一.修改某个表的字段类型及指定为空或非空 alter table 表名称 change 字段名称 字段名称 字段类型 [是否允许非空]; alter table 表名称 modify 字段名称 字段类 ...

  3. Linux重启命令

    Linux和windows不同,linux后台运行着许多进程,所以强制关机可能会导致进程的数据丢失使系统处于不稳定的状态.甚至在有的系统中会损坏硬件设备.而在系统关机前使用shutdown命令,系统管 ...

  4. PS制作黑暗墙面上的漂亮霓虹文字

    一.用ps软件打开砖墙背景素材. 二.复制一层,混合模式改为“正片叠底”,不透明度50%. 三.新建色相/饱和度调整图层,设置如下.打造夜间的气氛. 四.新建一个空白图层,设置前景色黑色.背景色白色, ...

  5. D3.js 入门学习(一)

    一.安装D3.js 1.网络连接 <script src="https://d3js.org/d3.v4.min.js"></script> 2.命令行安装 ...

  6. codeforces645B

    Mischievous Mess Makers CodeForces - 645B It is a balmy spring afternoon, and Farmer John's n cows a ...

  7. [SimplePlayer] 7. 多线程处理

    在前面的文章中,我们分别实现了视频图像解码.播放,音频解码.播放,现在则需要把这些功能组合起来.总体上来说,整个程序的功能可以分为两条线路:视频以及音频,两条线之间除了后续的同步操作之外基本没有任何关 ...

  8. 【数学建模】数模day13-灰色系统理论I-灰色关联与GM(1,1)预测

    接下来学习灰色系统理论. 0. 什么是灰色系统? 部分信息已知而部分信息未知的系统,我们称之为灰色系统.相应的,知道全部信息的叫白色系统,完全未知的叫黑色系统. 为什么采用灰色系统理论? 在给定信息不 ...

  9. spring中设计模式

    MVC模式 Model:pojo.数据库交互(业务数据和业务逻辑) View:Jsp(与用户交互页面) Controller:控制器(接收请求并决定调用哪个模块组件去处理请求,然后决定调用哪个视图(通 ...

  10. Python之常见算法介绍

    一.算法介绍 1. 算法是什么 算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制.也就是说,能够对一定规范的输入,在有限时间内获得所要求的输 ...