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. Android常用adb命令总结(二)

    adb shell 命令 简单点讲,adb 命令是 adb 这个程序自带的一些命令,而 adb shell 则是调用的 Android 系统中的命令,这些 Android 特有的命令都放在了 Andr ...

  2. 正确使用Spring Data JPA规范

    在优锐课的学习分享中探讨了关于,Spring Data JPA的创建主要是为了通过按方法名称生成查询来轻松创建查询. 但是,有时我们需要创建复杂的查询,而无法利用查询生成器.码了很多知识笔记分享给大家 ...

  3. IT兄弟连 HTML5教程 HTML5技术的应用现状及HTML5平台的兴起

    HTML5的优良特性很快被各种类型的网站利用,比如文件拖拽到网页上传功能,多数即使用HTML5提供的新属性就可以完成,来实现素材的免插件拖放.因此,HTML5技术实际上在国内已经获得了较广泛的应用与支 ...

  4. IT兄弟连 Java语法教程 关系运算符

    关系运算符用来判定一个操作数与另外一个操作数之间的关系.特别是,它们可以判定相等和排序关系.表7中列出了关系运算符. 表7  关系运算符 关系运算符的结果为布尔值.关系运算符最常用与if语句和各种循环 ...

  5. 02-Git远程仓库Github

    1.Git远程仓库 (Gitgub网站作为远程代码仓库时的操作和本地代码仓库一样的,只是仓库位置不同而已) 需要准备的东西: 1.准备Git源代码仓库https://github.com/ 2.准备李 ...

  6. MVC过滤器:自定义异常过滤器

    一.异常过滤器 异常筛选器用于实现IExceptionFilter接口,并在ASP.NET MVC管道执行期间引发了未处理的异常时执行.异常筛选器可用于执行诸如日志记录或显示错误页之类的任务.Hand ...

  7. 机器学习常见的几种评价指标:精确率(Precision)、召回率(Recall)、F值(F-measure)、ROC曲线、AUC、准确率(Accuracy)

    原文链接:https://blog.csdn.net/weixin_42518879/article/details/83959319 主要内容:机器学习中常见的几种评价指标,它们各自的含义和计算(注 ...

  8. JS常用标签

    1.由来 JavaScript的出现就是为了解决,不需要将所有的表单数据全部提交到服务器. 2.添加 加载Js代码的三种方式: 第一种:<script></script>标签里 ...

  9. SparkStreaming 整合kafka Demo

    这里使用的是低级API,因为高级API非常不好用,需要繁琐的配置,也不够自动化,却和低级API的效果一样,所以这里以低级API做演示 你得有zookeeper和kafka 我这里是3台节点主机 架构图 ...

  10. 7.python3实用编程技巧进阶(二)

    2.1.如何拆分含有多种分隔符的字符串 #2.1.如何拆分含有多种分隔符的字符串 s = 'ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz' #第一种方法 def my__ ...