P8260 [CTS2022] 燃烧的呐球

题意

已知 \(n\) 个顶点的有根树,以及 \(m\) 个二元组 \((x_i,y_i)\),其中 \(x_i,y_i\) 是树的顶点。

对于树的顶点 \(a,b\),定义 \(D(a,b)\) 为:在以 \(a\) 为根的子树中,但不在以 \(b\) 为根的子树中的顶点个数。

你需要求出以这些二元组为顶点的完全图的最小生成树,其中 \((x_i,y_i)\) 和 \((x_j,y_j)\) 之间的边权是 \(D(x_i,x_j)+D(x_j,x_i)+D(y_i,y_j)+D(y_j,y_i)\)。

\(1\le n\le 10^6,1\le m\le 10^5\)。

思路

题意有点绕。简单来说就是有 \(m\) 个二元组 \((x_i,y_i)\),两个二元组的距离是 \(d_{x_i,x_j} + d_{y_i,y_j}\),其中:

\[d_{x,y}=
\begin{cases}
siz_x-siz_y , & \text{x 是 y 的祖先} & (1)\\
siz_y-siz_x , & \text{y 是 x 的祖先} & (2) \\
siz_x+siz_y , & \text{x,y 互不为祖先} & (3)
\end{cases}
\]

要求完全图最小生成树,我们考虑 Boruvka 算法。

一共进行 \(\log n\) 轮,我们考虑每一轮如何扩展边。

找每个连通块最小的出边,可以找每个点最小出边然后取 \(\min\)。

如果只有 \(d_{x_i,y_i}\) 就好做,只需要考虑一维,但是它是二元组啊。

考虑变形一下,让两个二元组的距离拆开贡献。

\[\begin{cases}
(siz_{x_i}+siz_{y_i})+(siz_{x_j}+siz_{y_j}), & \text{$(x_i,y_i),(x_j,y_j)$ 都是情况 3}\\
(siz_{x_i}+siz_{y_i})+(siz_{x_j}-siz_{y_j}), & \text{$(x_i,y_i)$ 是情况 3,$(x_j,y_j)$ 是情况 1}\\
\dots & \text{(我不列了)}
\end{cases}
\]

我们只需要讨论 \(O(1)\) 种情况,预处理的时候排序。找最小的时候需要找 \(x\) 在 dfs 序上一段区间,\(y\) 在 dfs 序上另一段区间,而且所处连通块不同。

所处连通块不同的要求,我们可以维护无限制最优解和与这个解颜色不同的最优解两个信息。然后两维的话我们树套树,树链剖分。第一层树剖两只 \(\log\),第二层树剖也是两只 \(\log\),总的复杂度是 \(5 \log\),常数小,但是别说空间,时间就显然过不了。


以下“祖先”“后代”均指 \(j\) 是 \(i\) 的祖先/后代。

我们用树剖是因为我们找的 \(j\) 可能有一维是祖先关系,有一维是后代关系。我们分类讨论一下:当搜到 \(i\) 时,

  • \(i\) 两维都是 \(j\) 的祖先:可以在树上枚举第一维,然后第二维只有后代关系,按照第二维的时间戳做线段树合并。
  • \(j\) 两维都是 \(i\) 的祖先:在树上枚举第一维,按照第二维存到全局平衡二叉树里,然后每次在全局平衡二叉树上查询 \(i\) 到根的链。可以开一个桶记录全局平衡二叉树的操作以便撤销。
  • 一维祖先一维后代:在树上枚举祖先那一维,按照另一维的时间戳存到线段树里,查询就是线段树区间查询。

每种情况都是一只 \(\log\)。对于无关的维,因为无关的情况下 \(d\) 的符号都是 \(+\),所以我们可以按照无关算 \(i\) 与任意 \(j\) 的距离,算出来的答案不会正确答案更小。即:

  • 一维祖先一维无关:在树上枚举祖先那一维,维护最优答案。
  • 一维后代一维无关:在树上枚举后代那一维,维护每个子树的最优答案。
  • 两维均无关:所有点对找出两个最优的答案,一定有一个是合法的。

这几个都是没有 \(\log\) 的。

因此总时间复杂度 \(O(n \log^2)\)。

code

不会全局平衡二叉树,代码先鸽掉。

看起来不好写呢。不想写。我写的话,不过度压行,而且还不简洁,还要调试,不得 \(200\) 来行了?

唉,写吧写吧。

不想写了。继续鸽掉。

写了一点的代码如下:

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
constexpr int N=1e6+7,M=1e5+7,inf=0x3f3f3f3f;
int n,m;
int fa[N];
vector<int> son[N];
int x,y;
struct node {
int x,y;
}a[M];
int to[M];
int find(int u) { return to[u]==u ? u : to[u]=find(to[u]); }
int cnt;
struct mindis {
int v,d;
void _min(mindis b,int c=0) { if(b.d+c<d) v=b.v,d=b.d+c; }
}dis[M];//点对最近的点对编号及距离
int siz[N];
int dfn[N],dfn0,ed[N];
void dfs(int u) {
dfn[u]=++dfn0;
siz[u]=1;
for(int v : son[u]) {
dfs(v);
siz[u]+=siz[v];
}
ed[u]=dfn0;
}
vector<int> _vec[N],__vec[N];
auto vec=&_vec;
void init() {
rep(i,1,m) _vec[a[i].x].push_back(i), __vec[a[i].y].push_back(i);
dfs(1);
}
struct dseg {//动态开点线段树
void clear() { }
mindis query(int l,int r,int c,int u=1) { }
void insert(int x,int val,int c,int u=1) { }
}*tr1[N];
void merge(dseg *x,dseg *y) {//线段树合并 }
void solve1(int u) {//两维都在子树里找
tr1[u]->clear();
for(int v : son[u]) {
solve1(v);
merge(tr1[u],tr1[v]);
}
for(int id : (*vec)[u]) dis[id]._min(tr1[u]->query(dfn[a[id].y],ed[a[id].y],find(id)),siz[a[id].x]+siz[a[id].y]);
for(int id : (*vec)[u]) tr1[u]->insert(dfn[a[id].y],-siz[a[id].x]-siz[a[id].y],find(id));
}
#define fi first
#define se second
typedef pair<int,mindis> pim;
struct qjphecs {//全局平衡二叉树
mindis mn1[N],mn2[N];
int top_mn1,top_mn2;
vector<pim> vec_mn1,vec_mn2;
void clear() { }
void add(int u,int w,int c) { }
mindis query(int u,int c) { }
void ctrl_z(int la_mn1,int la_mn2) { }
}tr2;
void solve2(int u) {//两维都在祖先里找
int la_mn1=tr2.top_mn1,la_mn2=tr2.top_mn2;
for(int id : (*vec)[u]) dis[id]._min(tr2.query(a[id].y,find(id)),-siz[a[id].x]-siz[a[id].y]);
for(int id : (*vec)[u]) tr2.add(a[id].y,siz[a[id].x]+siz[a[id].y],find(id));
for(int v : son[u]) solve2(v);
tr2.ctrl_z(la_mn1,la_mn2);
}
struct seg {//线段树 vector<pim> vec_mn1,vec_mn2;
void clear() { }
mindis query(int l,int r,int c,int u=1) { }
void insert(int x,int val,int c,int u=1) { }
}tr3;
void solve3(int u) {//一维祖先一维后代
for(int id : (*vec)[u]) dis[u]._min(tr3.query(dfn[a[id].y],ed[a[id].y],find(id)),-siz[a[id].x]+siz[a[id].y]);
for(int id : (*vec)[u]) tr3.insert(dfn[a[id].y],siz[a[id].x]-siz[a[id].y],find(id));
for(int v : son[u]) solve3(v);
}
void solve() {
rep(i,1,m) dis[i]={0,inf};
vec=&_vec;
solve1(1);
tr2.clear();
solve2(1);
tr3.clear();
solve3(1);
}
void main() {
sf("%d%d",&n,&m);
rep(i,2,n) sf("%d",&fa[i]), son[fa[i]].push_back(i);
rep(i,1,m) sf("%d%d",&a[i].x,&a[i].y);
rep(i,1,m) col[i]=i;
cnt=m;
init();
while(cnt>1) solve();
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}

P8260 [CTS2022] 燃烧的呐球的更多相关文章

  1. AlloyRenderingEngine燃烧的进度条

    写在前面 Github: https://github.com/AlloyTeam/AlloyGameEngine HTML 5新增了progress标签,那么再去使用AlloyRenderingEn ...

  2. 【Pyrosim案例】02:简单燃烧

    1 案例说明 本案例介绍一个简单的燃烧模拟. 本案例通过指定热释放率(Heat Release Rate,HRR)来定义一个500kW的燃烧火焰.利用热释放率来定义燃烧火焰在火灾安全工程中描述火焰的一 ...

  3. css3圣诞雪景球

    本来想多做几个了 无奈最近太忙 于是模仿做了一个在codepen看到的圣诞雪景球   算是送给自己的圣诞礼物 演示地址:http://www.qdfuns.com/notes/26668/d5e177 ...

  4. js模拟抛出球运动

    js练手之模拟水平抛球运动 -匀加速运动 -匀减速运动 模拟运动有些基本的思路,当前所在点的坐标,元素的长宽是多少,向右/向下运动x/y增加,向上/向左运动x/y减少,运动的路程是多少,用什么方程进行 ...

  5. 敏捷开发与jira之燃烧图

    项目当前版本的燃烧图是下面这样的 存在的问题: 1.任务在版本起始时期之后再细化,造成了绿线一直在红线上面.解决方案:版本起始日期定为任务录入结束后的日期 2.工时录入不及时,没有实时反映当前项目组的 ...

  6. js版弹力球实例

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>弹 ...

  7. COGS396. [网络流24题]魔术球问题(简化版

    问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之和为完全平 ...

  8. 球谐光照(Spherical Harmonics Lighting)及其应用-实验篇

    简介 之前在一篇实时深度图优化的论文中看到球谐光照(Spherical Harmonics Lighting)的应用,在查阅了许许多多资料之后还是无法完全理解,我个人觉得如果之前对实时渲染技术不是很了 ...

  9. html5悬浮球效果

    自己想做一个自己的网站,觉得自适应的效果会好一点,但是放到手机端的话,菜单显示是个问题.所以自己试着写了一个悬浮球菜单的效果. 好了,上代码. 这里有四个文件要用: jqurey.js//因为基于jq ...

  10. 【OpenGL(SharpGL)】支持任意相机可平移缩放的轨迹球实现

    [OpenGL(SharpGL)]支持任意相机可平移缩放的轨迹球 (本文PDF版在这里.) 在3D程序中,轨迹球(ArcBall)可以让你只用鼠标来控制模型(旋转),便于观察.在这里(http://w ...

随机推荐

  1. DXF 最简单的一个文件生成两个直线一条直线放入BLOCKS中通过INSERT插入 (2)

    把#注解删除 0 SECTION 2 HEADER 9 $ACADVER 1 AC1009 9 $INSBASE 10 0.000000 20 0.000000 30 0.000000 9 $EXTM ...

  2. ETL中如何自定义规则

    一.ETL中的规则 在使用规则之前我们先来了解一下什么是规则,ETL中规则在很多组件中都能看见,可以理解为按照事前约定好的逻辑去执行,规则可以使得数据更加的规范统一,同时也不需要去纵向的修改底层代码, ...

  3. ICEE-Interface-SATA的数据与电源接口

    **SATA 数据接口(7pins) SATA 电源接口(15pins4Sections: +12V, +5V, +3.3V, GND) ** Sata实物:

  4. k8s与Docker-九五小庞

    随着k8s 作为容器编排解决方案变得越来越流行,有些人开始拿 Docker 和 k8s进行对比,不禁问道:Docker 不香吗? k8s 是kubernets的缩写,'8'代表中间的八个字符. 其实 ...

  5. VMware安装Centos7超详细过程(图文)-九五小庞

    本篇文章主要介绍了VMware安装Centos7超详细过程(图文),具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.软硬件准备 软件:推荐使用VMwear,我用的是VMwear 12 镜像:Ce ...

  6. Win10激活码密钥序列号如何使用永久激活的问题

    有雨林木风的小伙伴,已经使用上了win10正式版了,但是想要使用完整的系统功能,就必须激活才能使用.那么该如何永久激活Win10呢?有没有win10激活码又要如何使用呢?其实,直接从Win7/Win8 ...

  7. (译) 理解 Elixir 中的宏 Macro, 第二部分:宏理论

    Elixir Macros 系列文章译文 [1] (译) Understanding Elixir Macros, Part 1 Basics [2] (译) Understanding Elixir ...

  8. S32K148-lptmr配置

    S32K148自带的定时器,配置非常简单,有时候我会用它做任务周期,配置一个1ms定时器中断一次 初始化函数: void LPTMR_init(void) { LPTMR_DRV_Init(INST_ ...

  9. 超算CST DC分布式作业Main Controller 主控节点程序连接不上问题

    基础点: CST  DC  MC主控节点配置文件:/etc/xdg/CST AG/CST  DC Main Control2013.conf CST  DC SS求解器节点配置文件:/etc/xdg/ ...

  10. AI 加持实时互动|ZegoAvatar 面部表情随动技术解析

    01 AI"卷" 进实时互动 2021 年,元宇宙概念席卷全球,国内各大厂加速赛道布局,通过元宇宙为不同的应用场景的相关内容生态进行赋能.针对 "身份".&qu ...