原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html

前言

老年选手没有码力。

题解

先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$,点 x 在原树上的深度为 $d[x]$,那么

$$d[x]+d[y] - d[LCA(x,y)] - d'[LCA(x,y)] = \frac 12(D[x] + d[x]) + \frac 12 (D[y] + d[y]) - d'[LCA(x,y)]$$

于是我们考虑将分治区域内的节点在第二棵树上建虚树,并 DFS,每次维护一下子树中的 max(D[x] + d[x]) ,合并到父亲时顺便算一下答案。

类似于WC2018通道,这样做的时间复杂度是可以强行优化成 $O(n\log n)$ 的。

但是本题有更巧妙的做法。

考虑边分树这个数据结构。它具有几个性质:

1. 深度为 $O(\log n)$,准确地说是略大于 $\log _2 n $ 的,约等于 $\log_{1.5} n$。(嗯对,xza深度只开了20,被我hack了\kel)

2. 叶子节点个数为 $n$ 。

如果任取一种 DFS 序,并将其叶子按顺序排列,那么,两组节点的边分树合并的过程就可以看做以叶子 DFS 序为定义域的线段树合并。时间复杂度证明和线段树合并相同。写法也几乎相同。

于是,我们得到下面的优秀算法:

首先对第一棵树进行边分治,建出边分树。

然后对第二棵树进行 DFS,用“边分树合并”来支持子树合并操作。在边分树合并的同时计算答案。 我们要维护的值仅仅是边分时两半集合中节点的权值 max 。

时间复杂度 $O(n\log n)$ 。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=366677*2,S=N*13;
const LL LLINF=1e18;
struct Graph{
static const int M=N*2;
int cnt,y[M],fst[N],nxt[M],z[M];
void clear(){
cnt=1,clr(fst);
}
void add(int a,int b,int c){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt,z[cnt]=c;
}
void Add(int a,int b,int c){
add(a,b,c),add(b,a,c);
}
}t1,t2,T1;
#define Forg(g) for (int E=g.fst[x],y=g.y[E];E;E=g.nxt[E],y=g.y[E]) if (y!=pre)
int n,vcnt,ec;
int td[N],vis[N],pos[N];
LL d[N],val[27][N];
void GetT1(int x,int pre,LL D){
int t=x,v;
d[x]=D;
Forg(t1)
d[v=++vcnt]=D,T1.Add(t,v,0),T1.Add(v,y,t1.z[E]),t=v,GetT1(y,x,D+t1.z[E]);
}
namespace DT{
int size[N],Mx[N],nowsize,kx,ky,kz;
void GetRT(int x,int pre,int flen){
size[x]=1;
Forg(T1)
if (!vis[y])
GetRT(y,x,T1.z[E]),size[x]+=size[y];
Mx[x]=max(size[x],nowsize-size[x]);
if (!kx||Mx[x]<Mx[kx])
kx=x,ky=pre,kz=flen;
}
void dfs(int x,int pre,LL D,int w){
pos[x]|=w<<td[x],val[td[x]++][x]=D+d[x];
Forg(T1)
if (!vis[y])
dfs(y,x,D+T1.z[E],w);
}
void Divide(int x,int Size){
if (Size>1){
kx=0,nowsize=Size,GetRT(x,0,0),x=kx;
int y=ky,z=kz,tmp=size[x];
dfs(x,y,0,0),dfs(y,x,z,1);
vis[y]=1,Divide(x,tmp),vis[y]=0;
vis[x]=1,Divide(y,Size-tmp);
}
}
}
int rt[N],ls[S],rs[S];
LL Lmx[S],Rmx[S],ans=-LLINF;
int st[S],top,cnt=0;
int NewNode(){
return top?st[top--]:++cnt;
}
void RecNode(int x){
ls[x]=rs[x]=Lmx[x]=Rmx[x]=0,st[++top]=x;
}
void Ins(int &rt,int x,int D){
if (D<td[x]){
rt=NewNode(),Lmx[rt]=Rmx[rt]=-LLINF;
if (pos[x]>>D&1)
Ins(rs[rt],x,D+1),Rmx[rt]=max(Rmx[rt],val[D][x]);
else
Ins(ls[rt],x,D+1),Lmx[rt]=max(Lmx[rt],val[D][x]);
}
}
int Merge(int x,int y,LL add){
if (!x||!y)
return x|y;
ans=max(ans,max(Lmx[x]+Rmx[y],Rmx[x]+Lmx[y])/2+add);
Lmx[x]=max(Lmx[x],Lmx[y]),Rmx[x]=max(Rmx[x],Rmx[y]);
ls[x]=Merge(ls[x],ls[y],add),rs[x]=Merge(rs[x],rs[y],add);
return RecNode(y),x;
}
void Solve(int x,int pre,LL D){
ans=max(ans,d[x]-D),Ins(rt[x],x,0);
Forg(t2)
Solve(y,x,D+t2.z[E]),rt[x]=Merge(rt[x],rt[y],-D);
}
int main(){
n=read(),t1.clear(),t2.clear(),T1.clear();
For(i,1,n-1){
int x=read(),y=read(),z=read();
t1.Add(x,y,z);
}
For(i,1,n-1){
int x=read(),y=read(),z=read();
t2.Add(x,y,z);
}
vcnt=ec=n,GetT1(1,0,0),DT::Divide(1,vcnt),Solve(1,0,0);
cout<<ans<<endl;
return 0;
}

  

UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并的更多相关文章

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

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

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

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

  3. LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...

  4. 【UOJ#400】暴力写挂

    题目链接 题意 两棵树 , 求出下面式子的最大值. \[dep[u]+dep[v]-dep[LCA(u,v)]-dep'[LCA'(u,v)]\] Sol 边分治. 与第一棵树有关的信息比较多,所以对 ...

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

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

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

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

  7. BZOJ5341: [Ctsc2018]暴力写挂

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

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

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

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

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

随机推荐

  1. 用不上索引的sql

    1.使用不等于操作符(<>, !=) 大于可以.小于可以,between and 也可以 2.使用 is null 或 is not null 任何包含null值的列都将不会被包含在索引中 ...

  2. font-size 你所不知道的值

    说起 font-size  ,大家应该都知道是做什么的: CSS 属性指定字体的大小.因为该属性的值会被用于计算em和ex长度单位,定义该值可能改变其他元素的大小. 那么font-size 的值也是多 ...

  3. paroot忘记root密码

    打开虚拟机在倒计时进入系统前按下e键然后按照下图修改即可 crtl+x重启 mount –rw –o remount /保证磁盘可读写 然后执行passwd

  4. Map转url ,url转Map工具类

    /** * 将url参数转换成map * @param param aa=11&bb=22&cc=33 * @return */ public static Map<String ...

  5. 二十二、mysql索引原理详解

    背景 使用mysql最多的就是查询,我们迫切的希望mysql能查询的更快一些,我们经常用到的查询有: 按照id查询唯一一条记录 按照某些个字段查询对应的记录 查找某个范围的所有记录(between a ...

  6. 杭电OJ BestCoder28期1001Missing number问题(小技巧偏移法)

    1.先描述一下问题: 小yb有一个排列,但他不小心弄丢了其中的两个数字.现在他告诉你他现在手上还有哪些数字,需要你告诉他他丢了哪两个数字. 输入描述 有多组数据,第一行为数据组数T(T≤10). 对于 ...

  7. PAT_A1059

    这是一道素数因子分解的问题: 1.先打印素数表出来,以便后期使用,素数表的大小就是10^5级别就可以,因为输入的数是long int(即就是int而已),大小最大21亿(10^10量级的),我们这里素 ...

  8. vue之获取原生的dom的方式

    1.获取原生的DOM的方式 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  9. 苹果cms和海洋cms通用的百度主动推送工具

    百度主动推送的代码,不需要每天手动去添加地址推送,只要浏览器打开推送请求,不要关掉浏览器,程序自动帮你推送.(该插件只推送内容页,支持动态.伪静态.静态页面的推送,但这三种地址规则需要去代码里面自行拼 ...

  10. centos7下用命令安装node&pm2

    一.下载node安装包 1.wget https://npm.taobao.org/mirrors/node/latest-v12.x/node-v12.4.0-linux-x64.tar.gz 二. ...