[CTSC2018]暴力写挂

题面不错

给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离)

求最大的距离。

解决多棵树的问题就是降维了。

经典的做法是边分树合并

边分树结构类似0/1 trie

就是把边分树对于每个点拆开路径

合并两棵边分树同时可以得到两个边分树之间点对的路径的信息

感觉有点类似线段树合并。

根据“猫树”思想,两点间的路径一定经过边分树上LCA的那条边。(u,v不相等)

我们考虑在这个LCA处统计贡献

具体地,先对1树进行边分治

每个点初始的边分树是一条链,考虑对每个点构造出这个边分树。

开始只有根。

其实就是记录分治时候是在那个位置。

定义连接分治重心root深度较小的连通块为右部点,另一个为左部点

保存每个点作为左部点还是右部点

在每个之前最后一个加入位置lasi 下面加入左儿子或者右儿子。

在lasi位置保留这个信息vl,vr。初始是-inf

表示子树里所有的真实点在边分治这一层的左部、右部最大值。

左部点贡献权值:dis[x]

右部点贡献:dis[x]-dis[lca]

因为lca一定在右部点。

在第二棵树上枚举LCA z ,子树边分树合并上来(就类似树上的线段树合并)

合并时候max(vl(x)+vr(y)-dis'[z],vr(x)+vl(y)-dis'[z])更新答案。

然后vl(x)=max(vl(x),vl(y)) vr同理。按位取max

(注意没有pushup,因为这里是分治结构

可以发现,任意点对(u,v),一定在第二棵树上的LCA位置被考虑到,边分树合并时候,会在边分树LCA处尝试做出贡献。

大概初始的分治树:


代码:

注意:

1.边分树2*N个点,边数4*N,

边分治的vis数组开4*N。

2.处理u,v重合情况。

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/)output(x/);putchar(x%+'');}
template<class T>il void ot(T x){if(x<) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Miracle{
const int N=+;
const ll inf=1e18;
int n; struct tr{
int ls,rs;
ll vl,vr;
tr(){
ls=;rs=;vl=vr=-inf;
}
}t[N*];
ll ans;
int tot;
int rt[N];
ll nd;//now dis of lca'(u,v)
int merge(int x,int y){
// cout<<" merge "<<x<<" "<<y<<endl;
if(!x||!y) return x+y;
ans=max(ans,max(t[x].vl+t[y].vr,t[x].vr+t[y].vl)-nd);
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
t[x].vl=max(t[x].vl,t[y].vl);
t[x].vr=max(t[x].vr,t[y].vr);
return x;
} namespace tr1{
vector<int>to[N],val[N];
ll dis[*N];
struct node{
int nxt,to;
int val;
}e[*N];
int hd[*N],cnt=;
void add(int x,int y,int z){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
e[cnt].val=z;
}
int cur;
int d[*N];
void rebuild(int x,int fa){
int las=x;
for(reg i=;i<(int)to[x].size();++i){
int y=to[x][i];
if(y==fa) continue;
++cur;
add(las,cur,);
add(cur,las,);
add(cur,y,val[x][i]);
add(y,cur,val[x][i]);
las=cur;
rebuild(y,x);
}
}
void dfs(int x,int fa){
// cout<<" dfs tr1 "<<x<<" "<<fa<<endl;
d[x]=d[fa]+;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
dis[y]=dis[x]+e[i].val;
dfs(y,x);
}
}
int nowsz;
int vis[*N];
int las[*N];
int root,sz[*N];
int mi;
void fin(int x,int fa){
sz[x]=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[i]||y==fa) continue;
fin(y,x);
if(mi>max(sz[y],nowsz-sz[y])){
mi=max(sz[y],nowsz-sz[y]);
root=i;
}
sz[x]+=sz[y];
}
}
void dfs2(int x,int fa,int id,int typ){//min d's id//typ==0 : le ; typ==1 ri
sz[x]=;
if(d[id]>d[x]) id=x;
if(x<=n){
if(typ==){
++tot;
t[las[x]].ls=tot;
t[las[x]].vl=dis[x];
las[x]=tot;
}else{
++tot;
t[las[x]].rs=tot;
t[las[x]].vr=dis[x]-dis[id];
las[x]=tot;
}
} for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[i]||y==fa) continue;
dfs2(y,x,id,typ);
sz[x]+=sz[y];
}
}
void divi(int x){
if(nowsz==) return;
root=;
mi=0x3f3f3f3f;
fin(x,);
// cout<<" root "<<root<<" x "<<x<<endl;
int le=e[root].to,ri=e[root^].to;
if(d[le]<d[ri]) swap(le,ri);
// cout<<" le "<<le<<" ri "<<ri<<endl; vis[root]=vis[root^]=;
dfs2(le,,,);
dfs2(ri,,,);
nowsz=sz[le];
divi(le);
nowsz=sz[ri];
divi(ri);
}
void che(int x){
if(!x) return;
cout<<" nowcur "<<x<<endl;
cout<<" vl "<<t[x].vl<<" vr "<<t[x].vr<<" ls "<<t[x].ls<<" rs "<<t[x].rs<<endl;
che(t[x].ls);che(t[x].rs);
}
void main(){
int x,y,z; for(reg i=;i<n;++i){
rd(x);rd(y);rd(z);
to[x].push_back(y);val[x].push_back(z);
to[y].push_back(x);val[y].push_back(z);
}
cur=n;
rebuild(,);
// cout<<" rb "<<endl;
dfs(,);
// prt(dis,1,cur);
// prt(d,1,cur);
// cout<<" dfs "<<endl;
d[]=0x3f3f3f3f; nowsz=cur;
for(reg i=;i<=n;++i){
rt[i]=++tot;
las[i]=tot;
}
divi();
// che(3);
// che(4); // cout<<" divi "<<endl;
} }
namespace tr2{
ll dis[N];
struct node{
int nxt,to;
int val;
}e[*N];
int hd[N],cnt=;
void add(int x,int y,int z){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
hd[x]=cnt;
e[cnt].val=z;
}
void dfs(int x,int fa){
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
dis[y]=dis[x]+e[i].val;
dfs(y,x);
nd=dis[x];
// cout<<" xx "<<x<<" nd "<<nd<<endl;
rt[x]=merge(rt[x],rt[y]);
}
ans=max(ans,tr1::dis[x]-dis[x]);
}
void main(){
int x,y,z;
for(reg i=;i<n;++i){
rd(x);rd(y);rd(z);
add(x,y,z);add(y,x,z);
}
ans=-inf;
dfs(,);
} }
int main(){
rd(n);
tr1::main();
tr2::main();
ot(ans);
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/4/13 19:58:12
*/

合并时候,就是利用分治树的结构层层分离点对,在分治边的位置贡献。

进行降维。

[CTSC2018]暴力写挂——边分树合并的更多相关文章

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

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

  2. BZOJ5341: [Ctsc2018]暴力写挂

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

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

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

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

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

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

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

  6. 题解 「CTSC2018暴力写挂」

    题目传送门 题目大意 给出两个大小为 \(n\) 的树,求出: \[\max\{\text{depth}(x)+\text{depth}(y)-\text{depth}(\text{LCA}(x,y) ...

  7. LOJ #2533. 「CTSC2018」暴力写挂(边分治合并)

    题意 给你两个有 \(n\) 个点的树 \(T, T'\) ,求一对点对 \((x, y)\) 使得 \[ depth(x) + depth(y) - (depth(LCA(x , y)) + dep ...

  8. [CTSC2018]暴力写挂

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

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

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

随机推荐

  1. 【学亮IT手记】Java 8新特性实例介绍

    java8,也称为jdk1.8,于2014.03.18日发布,它支持函数式编程,新的js引擎,新的日期API,新的Stream Api等. 我们主要讨论以下几个新特性: ①Lambda表达式. 允许把 ...

  2. JSON Support in PostgreSQL and Entity Framework

    JSON 和JSONB的区别(What's difference between JSON and JSONB data type in PosgresSQL?) When should be use ...

  3. CDH 6.0.1 集群搭建 「After install」

    集群搭建完成之后其实还有很多配置工作要做,这里我列举一些我去做的一些. 首先是去把 zk 的角色重新分配一下,不知道是不是我在配置的时候遗漏了什么在启动之后就有报警说目前只能检查到一个节点.去将 zk ...

  4. shell自定义输入输出 read+echo

    自定义格式输入.输出(244)  输出:echo -e 解释转义字符 -n  回车不换行 \n   新的一行,等同于回车 \t 制表符 \r 回车 \b 回退 baskspace 删除键 演示\n \ ...

  5. CSS 常见的8种选择器 和 文本溢出问题

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

  6. ES 6 系列 - Proxy

    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以是一种“元编程”,即对编程语言进行编程. 简单地理解,就是在目标对象之前假设一层“拦截”,外界对改对象的访问,都必须先通过这层拦截 ...

  7. Nintex Forms Drop-Down "z-index"

    Now we’ve got the issue, that if we are working with a “Person-Column”, the drop-down where you can ...

  8. [洛谷日报第62期]Splay简易教程 (转载)

    本文发布于洛谷日报,特约作者:tiger0132 原地址 分割线下为copy的内容 [洛谷日报第62期]Splay简易教程 洛谷科技 18-10-0223:31 简介 二叉排序树(Binary Sor ...

  9. BZOJ2829信用卡凸包——凸包

    题目描述 输入 输出 样例输入 2 6.0 2.0 0.0 0.0 0.0 0.0 2.0 -2.0 1.5707963268 样例输出 21.66 提示 本样例中的2张信用卡的轮廓在上图中用实线标出 ...

  10. 网页调起QQ聊天

    将QQ账号换成正常的QQ号即可,要确保这个QQ支持临时会话 <a href="http://wpa.qq.com/msgrd?v=3&uin=QQ账号&site=qq& ...