题目传送门

题目大意

给出两个大小为 \(n\) 的树,求出:

\[\max\{\text{depth}(x)+\text{depth}(y)-\text{depth}(\text{LCA}(x,y)-\text{depth}^{'}(\text{LCA}^{'}(x,y)))\}
\]

\(n\le 3666666\),答案保证在 \(\text{long long}\) 范围内。

思路

边分治秒啊,终于学会了 边分树合并 了,在这里记录一下,以免后面忘掉了。

首先我们可(bu)以(ke)想(neng)到(de)一种 \(\Theta(n\log^2 n)\) 的做法,就是说我们可以先把式子化成:

\[\frac{1}{2}(\text{dist}(x,y)+\text{depth}(x)+\text{depth}(y)-2\text{depth}^{'}(\text{LCA}^{'}(x,y)))
\]

然后你发现如果不看最后那个东西的话,前面那个可以用边分治解决。然后我们发现我们可以对于第二棵树建出当前分治的点集的虚树,然后枚举某一个点作为 \(\text{LCA}^{'}(x,y)\) 。然后你就发现时间复杂度是 \(\Theta(n\log^2 n)\) ,而且常数巨大,无法通过此题。

然后我们需要引入一个叫「边分树」的东西,它跟点分树完全不一样。对于一个点我们维护一个它每一次被分治到的贡献,这道题目当中即为 \(\text{dis}(x)+\text{depth}(x)\)(\(\text{dis}(x)\) 即为 \(x\) 到分治点的距离),但是光是这样还不够,我们还需要能够更新答案,也就是说我们需要知道每次分治所属的块,这就是为什么需要是二叉树链的原因,如果某个点是它父亲的左节点,就说明他父亲是左边的块(左右其实是自己定义的,但这并不是很重要)。

考虑合并两个二叉树,对于同一相同路径走到的 \(x,y\) ,显然它们可以合成一个合法答案,直接连起来即可。

考虑如何弄到第二棵树上去,你发现如果钦定某一个点为 \(\text{LCA}\),那么就可以用已有的点与子子树进行合并更新答案即可,不过我们还需要一个点对它自己的贡献。

时间复杂度是 \(\Theta(n\log n)\),但是常数比较大。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define INF 0x7f7f7f7f7f
#define ll long long
#define MAXN 400250 char buf[1 << 25],*p1 = buf,*p2 = buf;
#define getchar() ((p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 25,stdin))) ? EOF : *p1 ++)
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} #define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define vl(x) tree[x].vl
#define vr(x) tree[x].vr struct node{
ll vl,vr;
int ls,rs;
node(){vl = vr = -INF;}
}tree[MAXN * 25]; int n,cnt,root[MAXN],las[MAXN]; namespace T1{
#define N 800250
ll dis[N],wei[N << 1];
int lim,ed,tot,toop = 1,to[N << 1],nxt[N << 1],vis[N << 1],siz[N],head[N];
void Add_Edge (int u,int v,ll w){
to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
}
void getdis (int u,int fa){for (Int i = head[u];i;i = nxt[i]) if (to[i] ^ fa) dis[to[i]] = dis[u] + wei[i],getdis (to[i],u);}
void findedge (int u,int fa,int Siz){
siz[u] = 1;
for (Int i = head[u];i;i = nxt[i]){
int v = to[i];
if (v == fa || vis[i]) continue;
findedge (v,u,Siz),siz[u] += siz[v];
int tmp = max (siz[v],Siz - siz[v]);
if (tmp < lim) lim = tmp,ed = i;
}
}
void dfs (int u,int fa,ll Dis,bool kase){
if (u <= n){
++ tot;
if (ls(las[u]) == -1) ls(las[u]) = tot;
else rs(las[u]) = tot;
if (kase == 0) ls(tot) = -1,vl(tot) = Dis + dis[u];
else rs(tot) = -1,vr(tot) = Dis + dis[u];
las[u] = tot;
}
for (Int i = head[u];i;i = nxt[i]){
int v = to[i];if (v == fa || vis[i]) continue;
dfs (v,u,Dis + wei[i],kase);
}
}
void divide (int u,int Siz){
if (Siz <= 1) return ;
lim = Siz,findedge (u,0,Siz),vis[ed] = vis[ed ^ 1] = 1;
int tx = to[ed],ty = to[ed ^ 1];
dfs (tx,0,0,0),dfs (ty,0,wei[ed],1);
divide (ty,Siz - siz[tx]),divide (tx,siz[tx]);
}
void Work (){
for (Int i = 1;i <= n;++ i) root[i] = las[i] = ++ tot,ls(root[i]) = -1;
getdis (1,0),divide (1,cnt);
}
#undef N
} namespace T2{
ll ans,now,wei[MAXN << 1];
int toop = 1,to[MAXN << 1],nxt[MAXN << 1],head[MAXN];
void Add_Edge (int u,int v,ll w){
to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
}
int Merge (int x,int y){
if (!x || !y) return x + y;
ans = max (ans,max (vl(x) + vr(y),vr(x) + vl(y)) - now);
ls(x) = Merge (ls(x),ls(y)),rs(x) = Merge (rs(x),rs(y));
vl(x) = max (vl(x),vl(y)),vr(x) = max (vr(x),vr(y));
return x;
}
void dfs (int u,int fa,ll dis){
for (Int i = head[u];i;i = nxt[i]){
int v = to[i];if (v == fa) continue;
dfs (v,u,dis + wei[i]),now = dis * 2,root[u] = Merge (root[u],root[v]);
}
ans = max (ans,(T1::dis[u] - dis) * 2);
}
void Work (){
dfs (1,0,0),write (ans / 2),putchar ('\n');
}
} #define N 800250 ll wei[N << 1];
int toop = 1,to[N << 1],nxt[N << 1],head[N]; void Add_Edge (int u,int v,ll w){
to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
} void dfs (int u,int fa){
for (Int i = head[u];i;i = nxt[i]){
int v = to[i];ll w = wei[i];
if (v == fa) continue;
dfs (v,u);
if (!las[u]) T1::Add_Edge (u,v,w),las[u] = u;
else T1::Add_Edge (las[u],++ cnt,0),T1::Add_Edge (las[u] = cnt,v,w);
}
} signed main(){
read (n),cnt = n;
for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),Add_Edge (u,v,w);
for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),T2::Add_Edge (u,v,w);
dfs (1,0),T1::Work ();T2::Work ();
return 0;
}

题解 「CTSC2018暴力写挂」的更多相关文章

  1. [LOJ#2553][CTSC2018]暴力写挂

    [LOJ#2553][CTSC2018]暴力写挂 试题描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

  2. [CTSC2018]暴力写挂——边分树合并

    [CTSC2018]暴力写挂 题面不错 给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离) 求最大的距离. 解决多棵树的问题就是降维了. 经典的做法是边分 ...

  3. BZOJ5341: [Ctsc2018]暴力写挂

    BZOJ5341: [Ctsc2018]暴力写挂 https://lydsy.com/JudgeOnline/problem.php?id=5341 分析: 学习边分治. 感觉边分治在多数情况下都能用 ...

  4. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  5. [CTSC2018]暴力写挂

    题目描述 www.lydsy.com/JudgeOnline/upload/201805/day1(1).pdf 题解 首先来看这个我们要最大化的东西. deep[u]+deep[v]-deep[lc ...

  6. 并不对劲的bzoj5341:loj2553:uoj400:p4565:[Ctsc2018]暴力写挂

    题目大意 有两棵\(n\)(\(n\leq366666\))个节点的树,\(T\)和\(T'\),有边权 \(dep(i)\)表示在\(T\)中\(i\)号点到\(1\)号点的距离,\(dep'(i) ...

  7. UOJ400/LOJ2553 CTSC2018 暴力写挂 边分治、虚树

    传送门--UOJ 传送门--LOJ 跟隔壁通道是一个类型的 要求的式子中有两个LCA,不是很方便,因为事实上在这种题目中LCA一般都是枚举的对象-- 第二棵树上的LCA显然是动不了的,因为没有其他的量 ...

  8. bzoj 5341: [Ctsc2018]暴力写挂

    Description Solution 边分治+边分树合并 这个题很多做法都是启发式合并的复杂度的,都有点卡 以前有个套路叫做线段树合并优化启发式合并,消掉一个 \(log\) 这个题思路类似,建出 ...

  9. Loj #2553. 「CTSC2018」暴力写挂

    Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

随机推荐

  1. js函数和封装

    $就是jquery对象,$()就是jQuery(),在里面可以传参数,作用就是获取元素 js对象与jQuery对象的区别:jQuery对象是一个数组,jQuery对象转为js对象:[0] 取第一个即可 ...

  2. 高德地图——添加标记的两种方法&删除地标记的两种方法

    添加标记: 1.marker.setMap(map); 2.marker.add([marker]); 删除标记: 1.marker.setMap(null); 2 map.remove([marke ...

  3. mysql switch语句

    SELECT CASE the_order_status WHEN 4 THEN '待收货' WHEN 5 THEN '已收货' ELSE '其他' END AS statuss ,order_id ...

  4. C# - 习题05_写出程序的输出结果o1.count

    时间:2017-08-24 整理:byzqy 题目:写出下列程序的输出结果: //原题程序如下: class Class1 { private static int count = 0; static ...

  5. JavaSE基础之Java中的数据类型

    基本数据类型:8个 整数类型(4):byte short int long 浮点类型(2):float double 字符类型:char 布尔类型:boolean 引用数据类型:3个 数组 类:cla ...

  6. Qt 程序发布以及打包成exe安装包

    一.简述 Qt 项目开发完成之后,需要打包发布程序,而因为用户电脑上没有 Qt 配置环境,所以需要将 release 生成的 exe 文件和所依赖的 dll 文件复制到一个文件夹中,然后再用 Inno ...

  7. SQL Server数据表设计编辑后无法保存处理办法

    关于使用 SQL Server 企业管理器,表[设计]界面,修改数据表字段或类型无法保存的问题处理过程: 使用SQL Server数据库的你是否遇到过每次数据库编辑工具内点击设计修改表字段或类型要保存 ...

  8. IP头详解

    IP包头长度(Header Length):长度4比特.这个字段的作用是为了描述IP包头的长度,因为在IP包头中有变长的可选部分.该部分占4个bit位,单位为32bit(4个字节),即本区域值= IP ...

  9. MySQL实战45讲(21--25)-笔记

    21 | 为什么我只改一行的语句,锁这么多? 加锁规则里面:包含了两个"原则".两个"优化"和一个"bug". 原则 1:加锁的基本单位是 ...

  10. 2.1Java基础

    2.1.9面向对象的三大特性(携程): 封装:把一个对象的属性隐藏在对象内部,外部对象不能直接访问这个对象的内部信息.但是可以提供一些可以被外界访问的方法来操作属性.就比如我们常常创建一个类,把他的属 ...