毫无$ Debug$能力

全世界就我会被卡空间.jpg

LOJ #2553

UOJ #400

Luogu P4565


题意

给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+deep(y)-deep(LCA(x,y))-deep'(LCA'(x,y))$尽量大

$ x$可以等于$ y$,点数不超过$ 366666$,边有边权


$ Solution$

枚举$T'$的一个点$ u$作为$LCA'(x,y)$,则$ x,y$必然在$u$的不同子树或者就是点$u$

$ ans=max(ans,deep[x]+deep[y]-deep[LCA(x,y)]-deep[u])$

直接做复杂度过大

考虑$ deep[x]+deep[y]-deep[LCA(x,y)]=\frac{1}{2}(deep[x]+deep[y]+dist(x,y))$

这样有根树就转化成了无根树

考虑对$ T$边分,将$ T$转化成可边分二叉树之后建出边分树(可能不用完全建出来?)然后记录每个叶子节点的路径

比如树

的其中一棵边分树为

用一个$ 01$串记录到每个叶子节点的路径

边分树有两个优美的性质

1.边分树的深度是$ log(n)$级别的

2.边分树每棵子树中的叶子节点在原树上一定连通

如果一条路径经过了边分树上某条边节点

则这条路径一定为(边节点左边子树的某个点节点到边节点的左端点)+(边节点右边子树的某个点节点到边节点的右端点)+边长度

而每对点$ (x,y)$的贡献的两倍为$ deep[x]+deep[y]+dist(x,y)$

因此我们可以在每个节点记录(其子树内点节点到它的路径长度+该点深度)的最大值$ Max$

然后在每个边节点处处理贡献

一开始边分树中所有点节点尚未被"激活"因此所有节点的$ Max$值都是$ -INF$

如果一个点节点可被使用则将其激活,将这个点在边分树的位置到边分树的根的这段路经的所有$ Max$进行更新

并与途中统计答案

枚举$ T'$的每个节点

新建一棵空的边分树(即所有点节点都尚未被激活)

将其于$ T'$内的所有子节点"激活"并在过程中统计答案是一种可行做法

复杂度过大无法接受

容易发现边分树很像线段树可以动态开点插入

加上可以像线段树合并一样,这个点的边分树可以依次合并其所有子节点的边分树

并在过程中统计答案

复杂度大概是$ O(n log n)$的

(好像还是讲不太清楚啊...)


$ my \ code$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define M 850010
#define rt register int
#define ll long long
using namespace std;
namespace fast_IO{
const int IN_LEN=,OUT_LEN=;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
}
using namespace fast_IO;
#define getchar() getchar_()
inline ll read(){
ll x=;char zf=;char ch=getchar();
while(ch!='-'&&!isdigit(ch))ch=getchar();
if(ch=='-')zf=-,ch=getchar();
while(isdigit(ch))x=x*+ch-'',ch=getchar();return x*zf;
}
void write(ll y){if(y<)putchar('-'),y=-y;if(y>)write(y/);putchar(y%+);}
void writeln(const ll y){write(y);putchar('\n');}
int k,m,n,x,y,z,cnt,ans,la,p[M],lg2[M*],up[][M*],q[M*],tt,dt[M];
ll deep0[M],deep2[M/];
struct node{int a,c;};
vector<node>e[M];
struct Tree{
int F[M],L[M],N[M*],a[M*],c[M*],k=;
void dfs(int x,int pre,int id){
q[++tt]=x;p[x]=tt;
for(rt i=F[x];i;i=N[i])if(a[i]!=pre){
if(id==)deep0[a[i]]=deep0[x]+c[i];
else deep2[a[i]]=deep2[x]+c[i];
dt[a[i]]=dt[x]+;
dfs(a[i],x,id);
q[++tt]=x;
}
}
void add(int x,int y,int z){
a[++k]=y;c[k]=z;
if(!F[x])F[x]=k;
else N[L[x]]=k;
L[x]=k;
}
void init(int x,int pre){
for(rt i=F[x];i;i=N[i])if(a[i]!=pre){
e[x].push_back({a[i],c[i]});
init(a[i],x);
}
}
int LCA(int x,int y){
int L=p[x],R=p[y];if(L>R)swap(L,R);int len=lg2[R-L+];
if(dt[up[len][L]]<dt[up[len][R-(<<len)+]])
return up[len][L];else return up[len][R-(<<len)+];
}
void LCA_init(){
for(rt i=;i<=tt;i++)up[][i]=q[i];
for(rt i=;i<=;i++)
for(rt j=;j<=tt;j++)
if(dt[up[i-][j]]<dt[up[i-][j+(<<i-)]])up[i][j]=up[i-][j];
else up[i][j]=up[i-][j+(<<i-)];
for(rt i=;i<=tt;i++)lg2[i]=lg2[i>>]+;
}
ll dis(int x,int y){return deep0[x]+deep0[y]-2ll*deep0[LCA(x,y)];}
}T0,T1,T2; void rebuild(){
for(rt i=;i<=n;i++){
if(e[i].size()<=){
for(auto j:e[i])T0.add(i,j.a,j.c*(j.a<=la)),T0.add(j.a,i,j.c*(j.a<=la));
continue;
}
int i0=++n,i1=++n;deep0[i0]=deep0[i1]=deep0[i];
for(rt j=;j<e[i].size();j++)if(j&)
e[i0].push_back({e[i][j].a,e[i][j].c});else e[i1].push_back({e[i][j].a,e[i][j].c});
T0.add(i,i0,);T0.add(i0,i,);T0.add(i,i1,);T0.add(i1,i,);
e[i].shrink_to_fit();
}
}
int id,nowmin,all,size[M];
bool vis[M];
void get(int x,int pre){
size[x]=;
for(rt i=T0.F[x];i;i=T0.N[i])if(!vis[i>>]&&T0.a[i]!=pre){
get(T0.a[i],x);size[x]+=size[T0.a[i]];
const int now=abs(all-size[T0.a[i]]-size[T0.a[i]]);
if(now<nowmin)nowmin=now,id=i;
}
}
string path[M];
int ww=;
int ls[M],rs[M],L[M],R[M],top,len[M];
void solve(int x,int sz,int&it,string s){
if(sz==)return path[x]=s,void();
nowmin=all=sz;if(!it)it=++ww;
get(x,x);vis[id>>]=; int xx=T0.a[id],yy=T0.a[id^],siz=size[xx];
L[it]=xx;R[it]=yy;len[it]=T0.c[id];
solve(xx,siz,ls[it],s+'');solve(yy,sz-siz,rs[it],s+'');
}
struct tree{
int ls,rs;ll Max;
}t[M*];
int bb=;
void insert(ll &ret,int &x,int y,int pl,int id){//在第x棵树插入第y个点
if(!x)x=++bb,t[x].Max=-100000000000000ll;
int sz=path[y].size();
if(pl==sz)return; if(path[y][pl]==''){
insert(ret,t[x].ls,y,pl+,ls[id]);
t[t[x].ls].Max=max(t[t[x].ls].Max,deep0[y]+T0.dis(y,L[id]));
}
else{
insert(ret,t[x].rs,y,pl+,rs[id]);
t[t[x].rs].Max=max(t[t[x].rs].Max,deep0[y]+T0.dis(y,R[id]));
}
ret=max(ret,t[t[x].ls].Max+t[t[x].rs].Max+len[id]);
}
int root[M];
int merge(ll &ret,int x,int y,int id){
if(!x)return y;if(!y)return x;
t[x].Max=max(t[x].Max,t[y].Max);
ret=max(ret,t[t[x].ls].Max+t[t[y].rs].Max+len[id]);
ret=max(ret,t[t[y].ls].Max+t[t[x].rs].Max+len[id]); t[x].ls=merge(ret,t[x].ls,t[y].ls,ls[id]);
t[x].rs=merge(ret,t[x].rs,t[y].rs,rs[id]); return x;
}
ll ret=-100000000000000ll;
void calc(int x,int pre){
ll ans=-100000000000000ll;
insert(ans,root[x],x,,);
for(rt i=T2.F[x];i;i=T2.N[i])if(T2.a[i]!=pre){
calc(T2.a[i],x);
root[x]=merge(ans,root[x],root[T2.a[i]],);
ret=max(ret,ans-2ll*deep2[x]);
}
}
int main(){
la=n=read();t[].Max=-100000000000000ll;
for(rt i=;i<n;i++){
x=read();y=read();z=read();
T1.add(x,y,z);
T1.add(y,x,z);
}
for(rt i=;i<n;i++){
x=read();y=read();z=read();
T2.add(x,y,z);
T2.add(y,x,z);
}
T2.dfs(,,);T1.init(,);rebuild();
tt=;T0.dfs(,,),T0.LCA_init();
int pl=;
solve(,n,pl,"");
calc(,);ret/=;
for(rt i=;i<=la;i++)ret=max(ret,deep0[i]-deep2[i]);
cout<<ret;
return ;
}

「CTSC2018」暴力写挂的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Python编码规范(PEP8)及奇技淫巧(不断更新)

    https://blog.csdn.net/MrLevo520/article/details/69155636

  2. 启动Hadoop总是需要输入密码的问题.

    start-all.sh 总是需要输入当前密码. 一开始以为是权限不够. 1.修改sudo配置文件 sudo visudo 增加 hduser ALL=(ALL) NOPASSWD:ALL 解决了 权 ...

  3. pytorch实现性别检测

    卷积神经网络的训练是耗时的,很多场合不可能每次都从随机初始化参数开始训练网络.   1.训练 pytorch中自带几种常用的深度学习网络预训练模型,如VGG.ResNet等.往往为了加快学习的进度,在 ...

  4. 英特尔关闭PC计算卡项目—插个卡片就能升级个人电脑

    在 2017 年的美国国际消费电子展上,电脑芯片巨头英特尔公司曾经推出一个名为“计算卡”的新产品,相当于把个人电脑的重要零部件整合到了一张信用卡大小的卡片设备中,未来用户升级个人电脑,只需要拔下旧卡片 ...

  5. JSP 学习总结 03 核心组件 Servlet

    1 Servlet 简绍 Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生 ...

  6. Auto Layout - BNR

    继续UIImageView - BNR篇. 通过Homepwner TARGETS -> General -> Deployment Info -> Devices中的iPhone改 ...

  7. 全文搜索引擎 ElasticSearch 还是 Solr?

    最近项目组安排了一个任务,项目中用到了全文搜索,基于全文搜索 Solr,但是该 Solr 搜索云项目不稳定,经常查询不出来数据,需要手动全量同步,而且是其他团队在维护,依赖性太强,导致 Solr 服务 ...

  8. 在SQL Server中如何进行UPDATE TOP .....ORDER BY?

    前言 今天在导入数据到系统后需要根据时间排序对刚导入的TOP N条进行数据更新,之前没遇到过UPDATE TOP...ORDER BY,以此作为备忘录. SQL SERVER之UPDATE TOP.. ...

  9. flask刷新token

    我们在做前后端分离的项目中,最常用的都是使用token认证. 登录后将用户信息,过期时间以及私钥一起加密生成token,但是比较头疼的就是token过期刷新的问题,因为用户在登录后,如果在使用过程中, ...

  10. 二十九、layui分页插件的使用

    <div id="page1"></div> <script> //开启分页 var page = 1; function findstoreL ...