题目大意

  给你三棵树,点数都是\(n\)。求

\[\max_{i,j}d_1(i,j)+d_2(i,j)+d_3(i,j)
\]

  其中\(d_k(i,j)\)是在第\(k\)棵数中\(i,j\)两点之间的距离。

  \(n\leq 100000\)

题解

  设\(d(i,j)=d_1(i,j)+d_2(i,j)+d_3(i,j),h_k(i)\)为\(i\)号点在第\(k\)棵树上的深度

一棵树

  树形DP。

  时间复杂度:\(O(n)\)

两棵树

  这是一道集训队自选题。

  点分治+动态点分治

  设这两个点在第一棵树中的LCA是\(p\),那么\(d(i,j)=h_1(i)+h_1(j)-2h_1(p)+d_2(i,j)\)

  在第二棵树中,对于每个点\(i\),建立一个新点\(i'\),在\(i\)和\(i'\)之间连一条边权为\(h_1(i)\)的边。

  这样\(d(i,j)=d_2(i,j)-2h_1(p)\)

  我们从下往上枚举\(p\),每次查询这棵子树的点在第二棵树中的直径。

  合并直径可以直接合并两个端点。

  时间复杂度:\(O(n\log n)\)

两棵树+一条链

  考虑对链分治。

  每次只求经过当前链中间那个点(或者那条边)的答案。

  \(d(i,j)=h_1(i)+h_1(j)-2h_1(p)+d_2(i,j)+|l_i-l_j|\)

三棵树

  考虑对第三棵树进行边分治。

  先把第三棵树转成二叉树

  然后直接边分治就行了。

  因为每个点的度数\(\leq 3\),所以边分治的复杂度是对的。

  求LCA可以用dfs序+ST表。

  还要维护当前部分在第一棵树的dfs序。

  时间复杂度:\(O(n\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c;
while((c=getchar())<'0'||c>'9');
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
int n;
vector<pil> g1[400010],g2[400010],g3[400010],g4[400010];
int lastson[400010];
int f1[400010];
int f2[400010];
int f3[400010];
ll d1[400010];
ll d2[400010];
ll d3[400010];
int dep1[400010];
ll w3[400010];
int st[400010];
int ed[400010];
int st1[400010];
int ed1[400010];
pli fs[21][400010];
pii fs1[21][200010];
int lo[400010];
int ti;
int ti1;
void dfs1(int x,int fa,ll dep,int dep2)
{
f1[x]=fa;
d1[x]=dep;
dep1[x]=dep2;
fs1[0][++ti1]=pii(dep2,x);
st1[x]=ti1;
for(auto v:g1[x])
if(v.first!=fa)
{
dfs1(v.first,x,dep+v.second,dep2+1);
fs1[0][++ti1]=pii(dep2,x);
}
ed1[x]=ti1;
}
void dfs2(int x,int fa,ll dep)
{
f2[x]=fa;
d2[x]=dep;
fs[0][++ti]=pli(dep,x);
st[x]=ti;
for(auto v:g2[x])
if(v.first!=fa)
{
dfs2(v.first,x,dep+v.second);
fs[0][++ti]=pli(dep,x);
}
ed[x]=ti;
}
void dfs3(int x,int fa,ll dep)
{
f3[x]=fa;
d3[x]=dep;
for(auto v:g3[x])
if(v.first!=fa)
{
w3[v.first]=v.second;
dfs3(v.first,x,dep+v.second);
}
}
void buildst()
{
int i,j;
for(i=1;i<=20;i++)
for(j=1;j+(1<<i)-1<=ti;j++)
fs[i][j]=min(fs[i-1][j],fs[i-1][j+(1<<(i-1))]);
for(i=1;i<=20;i++)
for(j=1;j+(1<<i)-1<=ti1;j++)
fs1[i][j]=min(fs1[i-1][j],fs1[i-1][j+(1<<(i-1))]);
lo[1]=0;
for(i=2;i<=ti;i++)
lo[i]=lo[i>>1]+1;
}
int queryst1(int x,int y)
{
int t=lo[y-x+1];
return min(fs1[t][x],fs1[t][y-(1<<t)+1]).second;
}
int querylca1(int x,int y)
{
if(st1[x]>st1[y])
swap(x,y);
return queryst1(st1[x],ed1[y]);
}
int queryst(int x,int y)
{
int t=lo[y-x+1];
return min(fs[t][x],fs[t][y-(1<<t)+1]).second;
}
int querylca(int x,int y)
{
if(st[x]>st[y])
swap(x,y);
return queryst(st[x],ed[y]);
}
ll c[400010];
ll querydist(int x,int y,ll z=0)
{
if(!x&&!y)
return -1;
if(!x||!y)
return 0;
return d2[x]+d2[y]-2*d2[querylca(x,y)]+c[x-n]+c[y-n]-2*z;
}
struct graph
{
int v[400010];
int t[400010];
int b[400010];
ll w[400010];
int h[200010];
int n;
graph()
{
n=0;
}
void add(int x,int y,ll z)
{
n++;
v[n]=y;
w[n]=z;
t[n]=h[x];
h[x]=n;
}
};
graph g;
void init()
{
int i;
int x,y;
ll z;
for(i=1;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&z);
g1[x].push_back(pil(y,z));
g1[y].push_back(pil(x,z));
}
for(i=1;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&z);
g2[x].push_back(pil(y,z));
g2[y].push_back(pil(x,z));
}
for(i=1;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&z);
g3[x].push_back(pil(y,z));
g3[y].push_back(pil(x,z));
}
dfs1(1,0,0,0);
for(i=1;i<=n;i++)
{
g2[i].push_back(pil(i+n,d1[i]));
g2[i+n].push_back(pil(i,d1[i]));
}
dfs2(1,0,0);
buildst();
dfs3(1,0,0);
for(i=1;i<=n;i++)
{
g.add(i,i+n,w3[i]);
g.add(i+n,i,w3[i]);
if(f3[i])
{
if(lastson[f3[i]])
{
g.add(i+n,lastson[f3[i]],0);
g.add(lastson[f3[i]],i+n,0);
}
else
{
g.add(i+n,f3[i],0);
g.add(f3[i],i+n,0);
}
lastson[f3[i]]=i+n;
}
}
}
int cmp1(int x,int y)
{
return st1[x]<st1[y];
}
int b[400010];
ll ans=0;
struct pp
{
int x,y;
ll v;
pp(int a=0,int b=0,ll c=-1)
{
x=a;
y=b;
v=c;
}
};
int operator >(pp a,pp b){return a.v>b.v;}
int operator <(pp a,pp b){return a.v<b.v;}
typedef pair<pp,pp> ppp;
ppp f[400010];
int x1,x2,num,sz;
ll xv;
int s[400010];
int tag[400010];
void dfs11(int x,int fa)
{
int i;
s[x]=1;
for(i=g.h[x];i;i=g.t[i])
if(!g.b[i]&&g.v[i]!=fa)
{
dfs11(g.v[i],x);
s[x]+=s[g.v[i]];
}
}
void dfs12(int x,int fa)
{
int i;
for(i=g.h[x];i;i=g.t[i])
if(!g.b[i]&&g.v[i]!=fa)
{
int mx=max(s[g.v[i]],num-s[g.v[i]]);
if(mx<sz)
{
sz=mx;
x1=x;
x2=g.v[i];
xv=g.w[i];
}
dfs12(g.v[i],x);
}
}
int op(int x)
{
return ((x-1)^1)+1;
}
void dfs13(int x,int fa,int b=1)
{
tag[x]=b;
int i;
for(i=g.h[x];i;i=g.t[i])
if(!g.b[i]&&g.v[i]!=fa)
{
int t=b;
if(g.v[i]==x2)
{
t=2;
g.b[i]=1;
g.b[op(i)]=1;
}
dfs13(g.v[i],x,t);
}
}
void dfs14(int x,int fa,ll dep)
{
c[x]=dep;
int i;
for(i=g.h[x];i;i=g.t[i])
if(!g.b[i]&&g.v[i]!=fa)
dfs14(g.v[i],x,dep+g.w[i]);
}
int sta[400010];
int top;
void updateans(pp a,pp b,ll z)
{
ans=max(ans,querydist(a.x,b.x,z));
ans=max(ans,querydist(a.x,b.y,z));
ans=max(ans,querydist(a.y,b.x,z));
ans=max(ans,querydist(a.y,b.y,z));
}
pp getmax(pp a,pp b,ll z)
{
return max(max(max(pp(a.x,a.y,querydist(a.x,a.y,z)),pp(a.x,b.x,querydist(a.x,b.x,z))),pp(a.x,b.y,querydist(a.x,b.y,z))),max(max(pp(b.x,b.y,querydist(b.x,b.y,z)),pp(a.y,b.x,querydist(a.y,b.x,z))),pp(a.y,b.y,querydist(a.y,b.y,z))));
}
void update(int x,int y)
{
updateans(f[x].first,f[y].second,d1[y]);
updateans(f[x].second,f[y].first,d1[y]);
f[y].first=getmax(f[x].first,f[y].first,d1[y]);
f[y].second=getmax(f[x].second,f[y].second,d1[y]);
}
void solve(int x,vector<int> &q)
{
if(q.empty())
return;
dfs11(x,0);
if(s[x]<=1)
return;
num=s[x];
sz=0x7fffffff;
dfs12(x,0);
dfs13(x,0);
dfs14(x1,0,0);
dfs14(x2,0,xv);
int last=0;
top=0;
int v1=q.front();
int v2=q.back();
int vlca=querylca1(v1,v2);
if(vlca!=v1)
{
sta[++top]=vlca;
f[vlca]=ppp();
}
for(auto v:q)
{
if(tag[v]==1)
f[v]=ppp(pp(v+n,0,0),pp());
else
f[v]=ppp(pp(),pp(v+n,0,0));
if(last)
{
int lca=querylca1(last,v);
while(dep1[lca]<dep1[sta[top]])
{
if(dep1[lca]<=dep1[sta[top-1]])
{
update(sta[top],sta[top-1]);
top--;
}
else
{
f[lca]=ppp();
update(sta[top],lca);
top--;
sta[++top]=lca;
}
}
}
sta[++top]=v;
last=v;
}
while(top>=2)
{
update(sta[top],sta[top-1]);
top--;
}
vector<int> q1,q2;
for(auto v:q)
if(tag[v]==1)
q1.push_back(v);
else
q2.push_back(v);
v1=x1;
v2=x2;
solve(v1,q1);
solve(v2,q2);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("uoj347.in","r",stdin);
freopen("uoj347.out","w",stdout);
#endif
scanf("%d",&n);
init();
vector<int> ss;
int i;
for(i=1;i<=n;i++)
ss.push_back(i);
sort(ss.begin(),ss.end(),cmp1);
solve(1,ss);
printf("%lld\n",ans);
return 0;
}

【UOJ347】【WC2018】通道 边分治 虚树 DP的更多相关文章

  1. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  2. LOJ 2339 「WC2018」通道——边分治+虚树

    题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...

  3. UOJ347 WC2018 通道 边分治、虚树

    传送门 毒瘤数据结构题qwq 设三棵树分别为$T1,T2,T3$ 先将$T1$边分治,具体步骤如下: ①多叉树->二叉树,具体操作是对于每一个父亲,建立与儿子个数相同的虚点,将父亲与这些虚点穿成 ...

  4. UOJ#347. 【WC2018】通道 边分治 虚树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ347.html 题意 有三棵树,边有边权. 对于所有点对 (x,y) 求在三棵树上 x 到 y 的距离之和 ...

  5. 【XSY3350】svisor - 点分治+虚树dp

    题目来源:NOI2019模拟测试赛(九) 题意: 吐槽: 第一眼看到题觉得这不是震波的完全弱化版吗……然后开开心心的码了个点分治 码到一半突然发现看错题了……心态崩了于是就弃疗手玩提答去了 于是就快乐 ...

  6. bzoj 3572世界树 虚树+dp

    题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...

  7. bzoj 2286 [Sdoi2011]消耗战 虚树+dp

    题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...

  8. 【BZOJ】2286: [Sdoi2011]消耗战 虚树+DP

    [题意]给定n个点的带边权树,每次询问给定ki个特殊点,求隔离点1和特殊点的最小代价.n<=250000,Σki<=500000. [算法]虚树+DP [题解]考虑普通树上的dp,设f[x ...

  9. [BZOJ5287][HNOI2018]毒瘤(虚树DP)

    暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...

随机推荐

  1. python之socket模块详解--小白博客

    主要是创建一个服务端,在创建服务端的时候,主要步骤如下:创建socket对象socket——>绑定IP地址和端口bind——>监听listen——>得到请求accept——>接 ...

  2. Java 自动装箱与拆箱(Autoboxing and unboxing)

    什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象实例的时候,我们会这样: Class a = ...

  3. hibernate添加数据时Exception in thread "main" org.hibernate.PropertyValueException: not-null property references a null or transient value: com.javakc.hibernate.test.entity.TestEntity.testName

    意思是,一个非null属性引用了一个null或瞬态值.就是在对应实体类配置文件hbm.xml中该属性配置了not-null="true",将其去掉即可.

  4. 第十二届湖南省赛G - Parenthesis (树状数组维护)

    Bobo has a balanced parenthesis sequence P=p 1 p 2…p n of length n and q questions. The i-th questio ...

  5. Django 中的Form表单认证

    一.Form表单   1.1 Form的几个功能 验证用户数据(显示错误信息) 初始化页面显示内容 HTML Form提交保留上次提交数据 生成HTML标签   1.2 创建表单类Form 1. 创建 ...

  6. stark组件之pop页面,按钮,url,页面

      1.Window open() 方法 2.admin的pop添加按钮 3.stark之pop功能 3.知识点总结 4.coding代码 1.Window open() 方法 效果图   2.adm ...

  7. snappy

    Snappy 是一个 C++ 的用来压缩和解压缩的开发包.其目标不是最大限度压缩或者兼容其他压缩格式,而是旨在提供高速压缩速度和合理的压缩率.Snappy 比 zlib 更快,但文件相对要大 % 到 ...

  8. 异常:fatal: unable to access 'https://git.oschina.net/pcmpcs/library.git/': Could not resolve host

    git  fork项目时出现的异常. 原因: 我以前用的是ssh地址做的远程通信地址,而这次是用的是https,因为很久没用,所以忘记了以前是用ssh的了.解决方案一:复制ssh协议的地址,然后再关联 ...

  9. CMMI摘要

    CMMI_百度百科https://baike.baidu.com/item/CMMI CMMI分为哪几个等级?CMMI等级介绍_百度经验https://jingyan.baidu.com/articl ...

  10. 便捷的ajax请求

    为什么要做这个呢?如果后端给的数据不单有JSON字符串,还有对象呢?这个时候我们就要每个都处理(JSON.parse).万一后端又改了,所有都是对象呢?如此一来我们就需要对我们的ajax进行封装. 这 ...