【UOJ#400】暴力写挂
题意
两棵树 , 求出下面式子的最大值。
\]
Sol
边分治。
与第一棵树有关的信息比较多,所以对第一棵树边分。
\(LCA\) 在分治中不好处理 ,因为我们要换根还要快速合并路径信息,那么把式子变个形:
\]
这个样子的话在边分的过程中就可以直接把 一个点的深度与它到当前分治边的某个端点的距离作为点权了。这样我们只需要快速求出在第二棵树中选出两个不在同一集合中的点使得他们的权值减去他们的 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】暴力写挂的更多相关文章
- 【CTSC2018】暴力写挂(边分治,虚树)
[CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...
- [CTSC2018]暴力写挂——边分树合并
[CTSC2018]暴力写挂 题面不错 给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离) 求最大的距离. 解决多棵树的问题就是降维了. 经典的做法是边分 ...
- [LOJ#2553][CTSC2018]暴力写挂
[LOJ#2553][CTSC2018]暴力写挂 试题描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...
- BZOJ5341: [Ctsc2018]暴力写挂
BZOJ5341: [Ctsc2018]暴力写挂 https://lydsy.com/JudgeOnline/problem.php?id=5341 分析: 学习边分治. 感觉边分治在多数情况下都能用 ...
- Loj #2553. 「CTSC2018」暴力写挂
Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...
- 「CTSC2018」暴力写挂
毫无$ Debug$能力 全世界就我会被卡空间.jpg LOJ #2553 UOJ #400 Luogu P4565 题意 给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+d ...
- uoj#400. 【CTSC2018】暴力写挂(边分治)
传送门 做一道题学一堆东西.jpg 猫老师的题--暴力拿的分好像比打挂的正解多很多啊--我纯暴力+部分分已经能有80了--正解没调对之前一直只有10分→_→ 先说一下什么是边分治.这个其实类似于点分治 ...
- UOJ#400. 【CTSC2018】暴力写挂
传送门 看到要求两棵树的 \(lca\) 深度不太好操作 考虑枚举第二棵树的 \(lca\),这样剩下的都是只和第一棵树有关的 而注意到 \(dis(x,y)=d(x)+d(y)-2d(lca(x,y ...
- UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并
原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$ ...
随机推荐
- 搭建第一个netty程序
来自action In netty 自己修改一点点 主要依赖 <dependencies> <dependency> <groupId>io.netty</g ...
- Jmeter之仅一次控制器
在进行压测时,往往登录只需要一次,其他的接口需要循环测试.这里就会用到控制器中的仅一次控制器.只要放置在控制器的请求,不管在哪种情况下,都只执行一次. 1.名称:可以标识在该控制器下主要完成的 工作内 ...
- Jmeter之简单控制器
在很多情况下,我们 需要将多个请求放置在一起,但是没有逻辑上的操作,这个时候就可以使用简单控制器了. 如 :
- Delphi 快速检测是否联网 判断网线是否拔开。 但是不能判断是否能上网
https://blog.csdn.net/chelen_jak/article/details/50204145 Delphi 快速检测是否联网 2015年12月07日 12:01:26 chele ...
- Win8.1+VS2013+WDK8.1+VirtualBox or VMware 驱动开发环境配置
https://blog.csdn.net/charlessimonyi/article/details/50904956 Win8.1+VS2013+WDK8.1+VirtualBox or VMw ...
- Scratch少儿编程系列:(九)音乐高级技巧
一.程序说明 本程序用来演奏音乐,相对于“Scratch少儿编程系列:(八)演奏简单音乐”而言,本节介绍的方法适用于复杂点的音乐. 二.程序流程图 为了更直观的描述上述过程,采用流程图的方式将猜数字的 ...
- C++ vector、list和deque的区别 (整理)
1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变.因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内 ...
- 006 Notepad++ 运行 C/C++
目录 0. 前言 1. 准备 2. 开工 setp 1 step 2 step 3 step 4 step 5 step 6 3. 修改与删除 3.1修改名称.快捷键 3.2 删除 4. 运行 5. ...
- 关于E980
1. 浪商官网上面的内容貌似有点问题 来源: https://www.inspurpower.com/product/others.php?f=E980 但是wiki 里面的东西: 其实只有12cor ...
- 线性表源码分享(c++),包含顺序表、单链表、循环链表、双向链表
---恢复内容开始--- 我是一个c++和数据结构的初学者,本文主要是把清华大学出版社的数据结构(用面向对象方法与c++语言描述)(第2版)这本书中第二章线性表的源码抄下来,在学习的过程中有助于加深印 ...