Loj #2553. 「CTSC2018」暴力写挂

题目描述

temporaryDO 是一个很菜的 OIer 。在 4 月,他在省队选拔赛的考场上见到了《林克卡特树》一题,其中 \(k = 0\) 的部分分是求树 \(T\) 上的最长链。可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路。

这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上。‘‘题目并不难。’’ 板板说。那充满磁性的声音,让 temporaryDO 全身充满了力量。

他决定:写一个枚举点对求 LCA 算距离的 \(k = 0\) 的 \(O(n^2\log\ n)\) 的部分分程序!于是, temporaryDO 选择以 \(1\) 为根,建立了求 LCA 的树链剖分结构,然后写了二重 for 循环枚举点对。

然而,菜菜的 temporaryDO 不小心开小了数组,于是数组越界到了一片神秘的内存区域。但恰好的是,那片内存区域存储的区域恰好是另一棵树 \(T′\) 。这样一来,程序并没有 RE ,但他求 \(x\) 和 \(y\) 的距离的时候,计算的是

\[depth(x) + depth(y) - (depth(LCA(x , y)) + depth′ (LCA′ (x, y)))
\]

最后程序会输出每一对点对 \(i, j (i \le j)\) 的如上定义的‘‘距离’’ 的最大值。

temporaryDO 的程序在评测时光荣地爆零了。但他并不服气,他决定花好几天把自己的程序跑出来。请你根据 \(T\) 和 \(T′\) 帮帮可怜的 temporaryDO 求出他程序的输出。

输入格式

第一行包含一个整数 \(n\) ,表示树上的节点个数;

第 \(2\) 到第 \(n\) 行,每行三个整数 \(x , y , v\) ,表示 \(T\) 中存在一条从 \(x\) 到 \(y\) 的边,其长度为 \(v\) ;

第 \(n + 1\) 到第 \(2n - 1 行\) ,每行三个整数 \(x , y , v\) ,表示 \(T′\) 中存在一条从 \(x\) 到 \(y\) 的边,其长度为 \(v\) 。

输出格式

输出一行一个整数,表示 temporaryDO 的程序的输出。

数据范围与提示

对于所有数据, \(n \le 366666 , |v| \le 2017011328\) 。

以前一直觉得边分治和点分治没什么区别,做了这道题才发现我太naive了。

首先题目中给的式子,不好看,所以我们把它变一下形:

\[\begin{align}
&dep_x+dep_y-dep_{lca(x,y)}
\\&=\frac{1}{2}(2*dep_x+2*dep_y-2*dep_{lca(x,y)})
\\&=\frac{1}{2}(dep_x+dep_y+dis_{x,y})
\end{align}
\]

\[ans=\frac{1}{2}(dep_x+dep_y+dis_{x,y}-2*dep'_{lca'(x,y)})
\]

于是我们可以枚举第二颗树的\(lca\),然后计算其子树之间的\(dep_x+dep_y+dis_{x,y}\)的最大值。后面部分就可以用边分治来维护。

可以类比点分治来理解边分治,就是在每个分治连通块中找到一条边使得边两端的连通块大小尽量平均。但是我们发现,一个菊花就可以把这个分治卡死。原因是某个点的度数太大了。于是我们考虑转成二叉树。具体就是每个点的上面连一个额外点,然后一个父亲节点连向其中一个儿子的额外点,几个儿子的额外点再连成一条线(代码一看就懂)。

假设分治中心边是\((x,y)\),我们像点分治那样统计分支块内每个点\(p\)到分治中心边的其中一个点的距离(\(dep_p+dis_{x,p}\))。这个距离有两种方向,分别对应\(x,y\)所在的连通块。我们发现,边分治的分治树是棵二叉树。所以对于每个点,我们可以开个二叉树,记录其在每一个分治连通块内的到中心点的距离。

然后就可以算答案了。当枚举了第二棵树上的\(lca\)的时候,合并每个子树的二叉树,同一种节点(到根路径相同)代表同一个分支连通块,我们可以在合并过程中更新答案。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 800005 using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n;
struct graph {
int to[N<<2],nxt[N<<2],dis[N<<2];
int h[N<<1],cnt=1;
void add(int i,int j,int d) {
to[++cnt]=j;
nxt[cnt]=h[i];
dis[cnt]=d;
h[i]=cnt;
}
void Init() {
memset(h,0,sizeof(h));
cnt=1;
}
}s,g; int vertex;
ll dep[N];
void build_edge(int v,int fr) {
int lst=v;
for(int i=s.h[v];i;i=s.nxt[i]) {
int to=s.to[i];
if(to==fr) continue ;
vertex++;
g.add(lst,vertex,0);
g.add(vertex,lst,0);
g.add(vertex,to,s.dis[i]);
g.add(to,vertex,s.dis[i]);
lst=vertex;
dep[to]=dep[v]+s.dis[i];
build_edge(to,v);
}
} int size[N<<1];
int sum,E,mx;
bool vis[N<<1]; void Find_edge(int v,int fr) {
size[v]=1;
for(int i=g.h[v];i;i=g.nxt[i]) {
int to=g.to[i];
if(vis[i]||to==fr) continue ;
Find_edge(to,v);
size[v]+=size[to];
int now=max(size[to],sum-size[to]);
if(mx>now) {
mx=now;
E=i;
}
}
} struct node {
ll dis;
int dir;
node() {}
node(ll _dis,int _dir) {dis=_dis,dir=_dir;}
}; vector<node>st[N<<1];
void statis(int v,int fr,ll dis,int dir) {
size[v]=1;
if(v<=n) {
st[v].push_back(node(dep[v]+dis,dir));
}
for(int i=g.h[v];i;i=g.nxt[i]) {
int to=g.to[i];
if(to==fr||vis[i]) continue ;
statis(to,v,dis+g.dis[i],dir);
size[v]+=size[to];
}
} void solve(int v) {
sum=size[v];
mx=1e9;
Find_edge(v,0);
int x=g.to[E],y=g.to[E^1];
vis[E]=vis[E^1]=1;
statis(x,0,0,0),statis(y,0,g.dis[E],1);
if(size[x]>1) solve(x);
if(size[y]>1) solve(y);
} int rt[N<<1];
int ls[N*20],rs[N*20];
ll lmx[N*20],rmx[N*20];
int tot;
void build_tree(int &v,vector<node>&a,int now) {
if(now==a.size()) return ;
v=++tot;
if(a[now].dir==0) {
lmx[v]=a[now].dis;
rmx[v]=-1ll<<60;
build_tree(ls[v],a,now+1);
} else {
rmx[v]=a[now].dis;
lmx[v]=-1ll<<60;
build_tree(rs[v],a,now+1);
}
} ll ans=-1ll<<60;
ll Dis;
int Merge(int a,int b) {
if(!a||!b) return a+b;
ans=max(ans,max(lmx[a]+rmx[b],lmx[b]+rmx[a])-2*Dis);
lmx[a]=max(lmx[a],lmx[b]);
rmx[a]=max(rmx[a],rmx[b]);
ls[a]=Merge(ls[a],ls[b]);
rs[a]=Merge(rs[a],rs[b]);
return a;
} void dfs2(int v,int fr,ll dis) {
ans=max(ans,2*dep[v]-2*dis);
for(int i=s.h[v];i;i=s.nxt[i]) {
int to=s.to[i];
if(to==fr) continue ;
dfs2(to,v,dis+s.dis[i]);
Dis=dis;
rt[v]=Merge(rt[v],rt[to]);
}
} int main() {
n=Get();
for(int i=1;i<n;i++) {
int a=Get(),b=Get(),c=Get();
s.add(a,b,c),s.add(b,a,c);
}
vertex=n;
build_edge(1,0);
size[1]=vertex;
solve(1);
for(int i=1;i<=n;i++) {
build_tree(rt[i],st[i],0);
}
s.Init();
for(int i=1;i<n;i++) {
int a=Get(),b=Get(),c=Get();
s.add(a,b,c),s.add(b,a,c);
}
dfs2(1,0,0);
cout<<ans/2;
return 0;
}

Loj #2553. 「CTSC2018」暴力写挂的更多相关文章

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

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

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

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

  3. 「CTSC2018」暴力写挂

    毫无$ Debug$能力 全世界就我会被卡空间.jpg LOJ #2553 UOJ #400 Luogu P4565 题意 给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+d ...

  4. loj#2552. 「CTSC2018」假面

    题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的 ...

  5. 【CTSC2018】暴力写挂(边分治,虚树)

    [CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...

  6. Loj #2554. 「CTSC2018」青蕈领主

    Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...

  7. 【学习笔记 边分树】【uoj400】【CTSC2018】暴力写挂

    题目 描述 ​ 有两棵树\(T\)和\(T'\),节点个数都为\(n\),根节点都为\(1\)号节点; ​ 求两两点之间 $$ \begin{align} depth(x) + depth(y) - ...

  8. LOJ#2552. 「CTSC2018」假面(期望 背包)

    题意 题目链接 Sol 多年以后,我终于把这题的暴力打出来了qwq 好感动啊.. 刚开始的时候想的是: 设\(f[i][j]\)表示第\(i\)轮, 第\(j\)个人血量的期望值 转移的时候若要淦这个 ...

  9. uoj#400. 【CTSC2018】暴力写挂(边分治)

    传送门 做一道题学一堆东西.jpg 猫老师的题--暴力拿的分好像比打挂的正解多很多啊--我纯暴力+部分分已经能有80了--正解没调对之前一直只有10分→_→ 先说一下什么是边分治.这个其实类似于点分治 ...

随机推荐

  1. css top,right,bottom,left设置为0有什么用?它和width:100%和height:100%有什么区别?

     壹 ❀ 引 当我们使用position属性时,总免不了与top,left,right,bottom四个属性打交道,那么这四个属性都设置为0时有什么用,与宽高设置100%又有什么区别?本文对此展开讨论 ...

  2. Kubernetes V1.15 二进制部署集群

    1. 架构篇 1.1 kubernetes 架构说明              1.2 Flannel网络架构图 1.3 Kubernetes工作流程             2. 组件介绍 2.1 ...

  3. wpf button style IsMouseOver

    <Style x:Key="workButtonStyle" TargetType="{x:Type Button}"> <Style.Tri ...

  4. Java自定义注解(1)

    Java注解简介 1. Java注解(Annotation) Java注解是附加在代码中的一些元信息,用于一些工具在编译. 运行时进行解析和使用,起到说明.配置的功能. 注解相关类都包含在java.l ...

  5. 服务端性能测试技能tree

    ALL: Left: Right: 摘抄一下(觉得不错) 以下来自百度百科 ---- 软件性能测试 软件性能测试是在交替进行负荷和强迫测试时常用的术语.理想的“软件性能测试”(和其他类型的测试)应在需 ...

  6. HTTP中的301、302、303、307、308

    结论 3XX开头的HTTP状态码都表示重定向的响应. 301.308是永久重定向:302.303.307是临时重定向. 301.302是http 1.0的内容,303.307.308是http1.1的 ...

  7. Gradle使用的简单了解

    Gradle 认识 参考博客:http://www.enjoytoday.cn/categorys/Gradle gradle是一个用于构建工程的工程配置脚本,它可以很便捷的帮助我们构建管理工程结构, ...

  8. iOS 禁用`URL Scheme`和`Universal Link`(通用链接)

    为什么要禁用URL Scheme和Universal Link(通用链接) 通常我们APP中都会嵌套一些web页面,有时我们的web页面会被DNS劫持从而跳转到其他APP中:或者是某些APP的Univ ...

  9. MongoDB 读偏好设置中增加最大有效延迟时间的参数

    在某些情况下,将读请求发送给副本集的备份节点是合理的,例如,单个服务器无法处理应用的读压力,就可以把查询请求路由到可复制集中的多台服务器上.现在绝大部分MongoDB驱动支持读偏好设置(read pr ...

  10. Bitbucket与git上传源码的使用方法

    本文链接:https://blog.csdn.net/nomisshe/article/details/19625555 Bitbucket使用方法   一.软件及SSH keys: 由于我的Bitb ...