题目链接

题意

两棵树 , 求出下面式子的最大值。

\[dep[u]+dep[v]-dep[LCA(u,v)]-dep'[LCA'(u,v)]
\]

Sol

边分治。

与第一棵树有关的信息比较多,所以对第一棵树边分。

\(LCA\) 在分治中不好处理 ,因为我们要换根还要快速合并路径信息,那么把式子变个形:

\[\frac{dep[u]+dep[v]+dis[u][v]}{2}-dep'[LCA'(u,v)]
\]

这个样子的话在边分的过程中就可以直接把 一个点的深度与它到当前分治边的某个端点的距离作为点权了。这样我们只需要快速求出在第二棵树中选出两个不在同一集合中的点使得他们的权值减去他们的 LCA 在第二棵树中的深度的最大值。

直接虚树 + 树形dp 就可以了。

其实这个题目还是比较板的,没有太大的思维难度。

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
struct edge{int to,next,w;};
const int N=4e5+10;
typedef long long ll;
typedef vector<int> ary;
const ll INF=1e16;
int n,Tn;
ll *Val,UPD;
int S[2][N];
ll ans=0;
namespace Tree2{
edge a[N<<1];
int head[N],cnt=0,bel[N];
int st[20][N<<1],id[N],log[N<<1],I=0,dep[N];ll dis[N];
inline void add(int x,int y,int z){a[++cnt]=(edge){y,head[x],z};head[x]=cnt;}
void dfs(int u,int fa){
st[0][++I]=u,id[u]=I;
for(int v,i=head[u];i;i=a[i].next){
v=a[i].to;if(v==fa) continue;
dep[v]=dep[u]+1,dis[v]=dis[u]+a[i].w;
dfs(v,u);st[0][++I]=u;
}
return;
}
inline int CK(int u,int v){if(!u||!v)return u|v;return dep[u]<dep[v]? u:v;}
inline int LCA(int u,int v){
if(u==v) return u;int l=id[u],r=id[v];if(l>r) swap(l,r);
int D=log[r-l+1]-1;
return CK(st[D][l],st[D][r-(1<<D)+1]);
}
inline ll Query(int u,int v){return dis[LCA(u,v)];}
void Build(){
int u,v,w;
for(int i=1;i<Tn;++i) init(u),init(v),init(w),add(u,v,w),add(v,u,w);
dfs(1,0);
for(int i=1;i<=I;++i) if((1<<log[i-1])<i) log[i]=log[i-1]+1;else log[i]=log[i-1];
for(int k=1;k<=log[I];++k)
for(int i=1;i+(1<<k)-1<=I;++i)
st[k][i]=CK(st[k-1][i],st[k-1][i+(1<<k-1)]);
cnt=0;Set(head,0);Set(bel,-1);
return;
}
namespace Vitual_Tree{
int que[N],tot=0;
int stk[N],top=0;
inline bool cmp(int u,int v){return id[u]<id[v];}
ll F[2][N];
inline void Insert(int u){
if(!top) return void(stk[++top]=u);
int lca=LCA(u,stk[top]);
if(lca==stk[top]) return void(stk[++top]=u);
while(top>=2&&id[stk[top-1]]>=id[lca]) add(stk[top-1],stk[top],0),--top;
if(stk[top]!=lca) add(lca,stk[top],0),stk[top]=lca;
stk[++top]=u;return;
}
void Dfs(int u){
F[0][u]=F[1][u]=-INF;
if(~bel[u]) F[bel[u]][u]=Val[u];
for(int v,i=head[u];i;i=a[i].next){
v=a[i].to;Dfs(v);
ans=max(ans,((F[0][u]+F[1][v]+UPD)/2)-dis[u]);
ans=max(ans,((F[1][u]+F[0][v]+UPD)/2)-dis[u]);
F[0][u]=max(F[0][u],F[0][v]);
F[1][u]=max(F[1][u],F[1][v]);
}
head[u]=0;bel[u]=-1;
}
void work(){tot=top=cnt=0;
for(int i=1;i<=S[0][0];++i) bel[S[0][i]]=0,que[++tot]=S[0][i];
for(int i=1;i<=S[1][0];++i) bel[S[1][i]]=1,que[++tot]=S[1][i];
sort(que+1,que+1+tot,cmp);
if(que[1]!=1) stk[top=1]=1;
for(int i=1;i<=tot;++i) Insert(que[i]);
while(top>1) add(stk[top-1],stk[top],0),--top;
Dfs(1);return;
}
}using Vitual_Tree::work;
}
namespace Tree1{
const int MAXN=N<<2;
edge a[MAXN<<1];
int head[MAXN],cnt=0,size[MAXN];ll dep[MAXN],dis[MAXN],Ret[MAXN];
bool vis[MAXN];
inline void add(int x,int y,int z){a[cnt]=(edge){y,head[x],z};head[x]=cnt++;}
inline void add_edge(int u,int v,int z){add(u,v,z),add(v,u,z);return;}
ary son[MAXN];
void dfs(int u,int fa){
for(int v,i=head[u];~i;i=a[i].next){
v=a[i].to;if(v==fa) continue;
son[u].push_back(v);dep[v]=dep[u]+a[i].w;Ret[v]=a[i].w;
dfs(v,u);
}
return;
}
int Cedge;
inline void Rebuild(){
Tn=n;cnt=0;Set(head,-1);
for(int i=1;i<=n;++i) {
int snum=son[i].size();
if(snum<=2) {
for(int j=0;j<snum;++j) add_edge(i,son[i][j],Ret[son[i][j]]);
}else{
int sl=++n;int sr=++n;
add_edge(i,sl,0),add_edge(i,sr,0);
for(int j=0;j<snum;++j) {
if(j&1) son[sl].push_back(son[i][j]);
else son[sr].push_back(son[i][j]);
}
}
}
return;
}
inline void Build(){
Set(head,-1);int u,v,w;
for(int i=1;i<n;++i) {init(u),init(v),init(w);add_edge(u,v,w);}
dfs(1,0);Rebuild();
return;
}
int Mx,Tot;
void Find(int u,int fa){
size[u]=1;
for(int v,i=head[u];~i;i=a[i].next){
v=a[i].to;if(v==fa||vis[i>>1]) continue;
Find(v,u);size[u]+=size[v];
int dat=max(size[v],Tot-size[v]);
if(dat<Mx) Mx=dat,Cedge=i;
}return;
}
void Dfs(int u,int fa,int*S,ll Dep){
dis[u]=dep[u]+Dep;
if(u<=Tn) S[++S[0]]=u;
for(int v,i=head[u];~i;i=a[i].next){
v=a[i].to;if(v==fa||vis[i>>1]) continue;
Dfs(v,u,S,Dep+a[i].w);
}
return;
}
inline void Divide(int u,int siz){
Mx=1e9;Tot=siz;Find(u,0);
if(Mx>=1e9) return;vis[Cedge>>1]=1;
int rtl=a[Cedge].to,rtr=a[Cedge^1].to;UPD=a[Cedge].w;
S[0][0]=S[1][0]=0;Dfs(rtl,0,S[0],0),Dfs(rtr,0,S[1],0);
Tree2::work();int szl=size[rtl];
Divide(rtl,szl);Divide(rtr,siz-szl);
return;
}
inline void Solve(){Val=dis;Divide(1,n);return;}
}
int main()
{
init(n);
Tree1::Build();
Tree2::Build();
Tree1::Solve();
for(int i=1;i<=Tn;++i) ans=max(ans,Tree1::dep[i]-Tree2::dis[i]);
cout<<ans<<endl;
return 0;
}

【UOJ#400】暴力写挂的更多相关文章

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

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

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

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

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

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

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

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

  5. BZOJ5341: [Ctsc2018]暴力写挂

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

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

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

  7. 「CTSC2018」暴力写挂

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

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

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

  9. UOJ#400. 【CTSC2018】暴力写挂

    传送门 看到要求两棵树的 \(lca\) 深度不太好操作 考虑枚举第二棵树的 \(lca\),这样剩下的都是只和第一棵树有关的 而注意到 \(dis(x,y)=d(x)+d(y)-2d(lca(x,y ...

  10. UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

    原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$ ...

随机推荐

  1. Java中class的声明

    在Java中下面Class的声明哪些是错误的?(A,B,C)   A:public abstract final class Test { abstract void method();} B:pub ...

  2. OpenStack Placement Project

    目录 文章目录 目录 背景 Placement 简介 基本概念 数据模型解析 Command Line Placement Web Application 的实现与分析 Placement 在启动虚拟 ...

  3. 测开之路一百二十七:flask之构造response对象

    可以使用flask.make_response构造自定义响应信息 构造一个响应信息为文本,状态码为404 响应 响应数据为json,状态码为200 返回html # coding:utf-8from ...

  4. 配置文件pytest.ini

    前言 pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行. ini配置文件 pytest里面有些文件是非test文件 py ...

  5. C 语言结构体 struct 及内存对齐

    struct 结构体 对于复杂的数据类型(例如学生.汽车等),C 语言允许我们将多种数据封装到一起,构成新类型. 跟面向对象语言中的对象相比,结构体只能包含成员变量,不支持操作. #include & ...

  6. IntlliJ IDEA 注册码获取或离线破解

    JB 的软件还是挺好用的,建议有钱的话支持正版.. IntelliJ IDEA 有开源版,但是要想玩企业级开发,还是得用收费版. 不管哪种方式,使用前都需要把"0.0.0.0 account ...

  7. 以非root身份安装Python的Module或者Package以及pip安装指定路径

    因为要远程访问公司的服务器,没有sudo的权限,所以在安装python的一些包的时候就不能安去默认路径了(比如以/usr/local/lib/为prefix的路径). 一般来讲用easy_instal ...

  8. CSU1081有向图BFS

    集训队分组 Description中南大学ACM的暑期集训马上就要开始了,这次集训会将全体N名集训队员(编号分别为1, 2, …, N)按集训选拔赛的排名分成两组,前K名队员分入A组,其余队员分入B组 ...

  9. pandas DataFram的insert函数

    原文链接:https://blog.csdn.net/yanwucao/article/details/80211984 DataFrame.insert(loc, column, value, al ...

  10. LeetCode链表简单题

    一.21合并两个有序链表 代码如下: class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNod ...