题目大意

  给你三棵树,点数都是\(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. Dedekind整环上的有限生成模的分类

    以下内容本想载于我的代数数论初步当中,但是与整体风格不符,所以换到这里来,为了排版上的方便,在注释掉之前用截图留存. 附:参考文献

  2. sql面试学到新内容

    1.事物的保存点 MYSQL可以让我们对事务进行部分回滚,就是在事务里调用SAVEPOINT语句来设置一些命名标记.如果想要回滚到那个标记点位置,需要使用ROLLBACK语句来指定哪个保存点. mys ...

  3. 剑指Offer-- 二叉搜索树中和为某一值的路径

    输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径. 本身题目不是很难,但是因为刚接触pyhon,对一些对象的传 ...

  4. ES6 Promise 详解

    一.概念 Promise,从语法上来讲,它是一个对象,是一个构造函数,可以获取 异步操作 的信息. 简单来讲,就是用同步的方式写异步代码,用来解决回调问题. 二.特点 Promise 对象有两个特点: ...

  5. lower_bound函数与upper_bound函数

    头文件 : algorithm vector<int>a a中的元素必须升序,用的是二分 lower_bound(a.begin(),a.end(),k) 返回a容器中,最右边的小于等于k ...

  6. python三数之和

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. ...

  7. Survey项目总结

    1.Ioc深入理解 Inverse of control org.springframework.scheduling.quartz.SchedulerFactoryBean org.mybatis. ...

  8. js-其他跨域技术(JSONP`Comet)

    ###1.  JSONP JSONP由两部分组成:回调函数和数据 JSONP是通过动态<script>元素来使用的,使用时可以为src属性指定一个跨域URL eg: function ha ...

  9. CMake--List用法

    list(LENGTH <list><output variable>) list(GET <list> <elementindex> [<ele ...

  10. DLNA流媒体设置