毫无$ 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. 单元测试(qunit)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  2. @getMapping与@postMapping

    首先要了解一下@RequestMapping注解. @RequestMapping用于映射url到控制器类的一个特定处理程序方法.可用于方法或者类上面.也就是可以通过url找到对应的方法. @Requ ...

  3. SQL瓶颈分析,以及适应最佳执行计划的探讨

    原文地址:   https://blog.csdn.net/daiqiulong2/article/details/86546446?tdsourcetag=s_pcqq_aiomsg 年纪大了,慢慢 ...

  4. (转)lwip TCP client & FreeRTOS 打开TCP 的 保活机制 LWIP_TCP_KEEPALIVE==1

    参考大神教程:http://blog.sina.com.cn/s/blog_62a85b950101aw8x.html   老衲五木 :http://blog.sina.com.cn/s/blog_6 ...

  5. BZOJ4034: [HAOI2015]树上操作

    这题把我写吐了...代码水平还是太弱鸡了啊... 这题就是先给你一些点,以及点权.然后给你一些向边构成一颗树,树的根节点是1. 然后给定三个操作 第一个是把指定节点的权值+W 第二个是把指定节点X为根 ...

  6. Jquery2--属性相关的操作

    知识点总结 1.属性 属性(如果你的选择器选出了多个对象,那么默认只会返回出第一个属性). attr(属性名|属性值) - 一个参数是获取属性的值,两个参数是设置属性值 - 点击加载图片示例 remo ...

  7. 删除 node_modules文件夹cmd指令

    方法一: npm install rimraf -g rimraf node_modules 方法二: rmdir /s/q your_app_dir 方法三: rm -f /node_modules

  8. 初步了解Bootstrap4

    Bootstrap 是全球最受欢迎的前端组件库,用于开发响应式布局.移动设备优先的 WEB 项目. Bootstrap4 目前是 Bootstrap 的最新版本,是一套用于 HTML.CSS 和 JS ...

  9. Mock6 moco框架中如何加入header

    新建一个 startupWithHeader.json,这次在request里面添加了headers属性 [ { "description": "这是一个带header的 ...

  10. selenium技术博客

    1.java+selenium+详细的api说明和seleniumGrid使用,缺点是代码不够好看. http://www.cnblogs.com/yytesting/p/5714175.html