洛谷P4220 [WC2018]通道(边分治+虚树)
题面
题解
代码不就百来行么也不算很长丫
虽然这题随机化贪心就可以过而且速度和正解差不多不过我们还是要好好学正解
前置芝士
边分治
米娜应该都知道点分治是个什么东西,而边分治,顾名思义就是对边进行分治,即每次选出一条“子树中点的个数的最大值最小”的边,处理所有经过这条边的路径的贡献,然后割掉这条边之后对子树递归下去就好了
然而出题人给你一个菊花图就能把你卡得不要不要的
我们发现上述策略在一个二叉树上是最优的,因为割掉边之后左右子树大小都会变为原来的一半
于是这里就需要多叉树转二叉树,具体的流程可以看\(shadowice\)巨巨的图
这样的话点数最多变成\(2n\),但是分治的时候复杂度就有保证了
虚树
咱对这东西也不熟所以请米娜自己去找吧
本题
本题要求我们最大化
\]
我们在第一棵树上边分,处理所有经过\((u,v)\)这条边的路径的贡献,那么肯定满足\(x\)和\(u\)在同一联通块(以下称为黑点),\(y\)和\(v\)在同一个联通块(以下称为白点),并且因为\((u,v)\)的长度是一个定值,那么我们需要最大化
\]
把它改写一下
\]
上面的柿子里是只和\(x,y\)有关的,那么我们还需要减去
\]
因为我们已经选定了\(u,v\),所以我们可以快速预处理出\(w(x)=dis1(x,u/v)+dep2(x)+dep3(x)\),那么就变成最大化
\]
发现每一次分治的时候满足条件的\(x,y\)都是一个点集,那么我们可以把这些点集放到第二颗树上做一个虚树
在虚树上,我们可以枚举\(LCA\),设为\(p\),那么此时\(dep2(LCA2(x,y))\)就是个定值了,我们需要求出两个点\(x,y\),其中\(x\)是黑点,\(y\)是白点,且\(x,y\)不在\(p\)的同一子树内,并且最大化
\]
首先你需要知道一个结论:如果有一棵树\(T\),边权非负,直径的端点为\(u,v\),和一棵树\(G\),直径的端点为\(a,b\),那么用一条边将它们连接起来之后(无论把两棵树的那两个节点相连),直径一定为\(uv,ua,ub,va,vb,ab\)六条路径中的一条(这里的直径定义为不存在路径比它长,不过有可能有路径和它长度相等)
证明的话可以感性理解一下
然后这里就需要用到一个推论:对于两个树上的点集\(A,B\),\(A\)的直径端点为\(u,v\),\(B\)的直径端点为\(a,b\),那么一条左右端点分别在\(A,B\)中的最长的路径必为\(ua,uv,va,vb\)四条路径中的一条
然而放到这里有什么用么?\(w(x)+w(y)-2\times dep3(LCA3(x,y))\)怎么看也不像是树上两点的距离啊?
那么我们把它化一下,变成
\]
那么我们可以看做是在第三棵树上,在\(x\)下面挂一个虚点\(x'\),\((x,x')\)的长度为\(dis1(x,u)+dep2(x)\),\(y\)同理,这样能看做是树上的一条路径了,也就是说我们关于树上路径的那个结论成立
然后就没有然后了,码吧
似乎有大佬写了随机化贪心的做法,这里说一下好了,就是每一次枚举根节点算出三棵树上其他点到它距离总和的最大值,用距离最大的点来更新根节点,然后每隔几次之后重新\(rand\)根节点……我调了调之后速度已经和我的正解差不多了(虽然会被\(uoj\)上的\(hack\)数据卡掉不过比赛的时候可没有这么强的数据啊)
虽然我估计考场上我可能这种随机化贪心都打不出来
可以对比一下下面两份代码的码量……
这份是随机化贪心,洛谷上能过,\(uoj\)会被\(hack\)
// luogu-judger-enable-o2
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define CL 1000.0*clock()/CLOCKS_PER_SEC
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
R ll res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=1e5+5;
struct eg{int v,nx;ll w;};
struct Gr{
eg e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
ll dis[N];
void dfs(int u,int fa){go(u)v!=fa?(dis[v]=dis[u]+e[i].w,dfs(v,u),0):0;}
}g[3];
ll res,w,las;bool vis[N];int n,u,v,c;
inline int rd(){return 1ll*rand()*rand()%n+1;}
void qwq(){
fp(rt,1,n){
g[0].dis[rt]=g[1].dis[rt]=g[2].dis[rt]=0;
g[0].dfs(rt,0),g[1].dfs(rt,0),g[2].dfs(rt,0);
fp(j,1,n)cmax(res,g[0].dis[j]+g[1].dis[j]+g[2].dis[j]);
}
printf("%lld\n",res);
}
int main(){
srand(19260817);
// freopen("testdata.in","r",stdin);
double st=CL;
n=read();
fp(i,1,n-1)u=read(),v=read(),w=read(),g[0].add(u,v,w),g[0].add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),g[1].add(u,v,w),g[1].add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),g[2].add(u,v,w),g[2].add(v,u,w);
if(n<=3500)return qwq(),0;
while(CL-st<=3400){
int rt=rd();las=res;
while(vis[rt])rt=rd();
fp(i,1,5){
if(vis[rt])break;
vis[rt]=1,g[0].dis[rt]=g[1].dis[rt]=g[2].dis[rt]=0;
g[0].dfs(rt,0),g[1].dfs(rt,0),g[2].dfs(rt,0);
ll mx=0;
fp(j,1,n)
cmax(res,g[0].dis[j]+g[1].dis[j]+g[2].dis[j]),
!vis[j]&&cmax(mx,g[0].dis[j]+g[1].dis[j]+g[2].dis[j])?rt=j:0;
}
las==res?++c:c=0;
if(c>=20)break;
}
printf("%lld\n",res);
return 0;
}
然后下面这份是正解
虽然它们都说求\(LCA\)要用\(ST\)表然而亲测用树剖似乎速度差不多?
// luogu-judger-enable-o2
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
R ll res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=5e5+5;
int lg[N],s[2][N],top[2],ty[N];ll w[N],res;
int n;
namespace tree3{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int top[N],dep[N],fa[N],sz[N],son[N];ll dis[N];
void dfs1(int u){
sz[u]=1,dep[u]=dep[fa[u]]+1;
go(u)if(v!=fa[u]){
dis[v]=dis[u]+e[i].w,fa[v]=u,dfs1(v),sz[u]+=sz[v];
sz[v]>sz[son[u]]?son[u]=v:0;
}
}
void dfs2(int u,int t){
top[u]=t;if(!son[u])return;
dfs2(son[u],t);
go(u)!top[v]?(dfs2(v,v),0):0;
}
inline int LCA(R int u,R int v){
while(top[u]!=top[v]){
dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
inline ll Dis(R int u,R int v){return w[u]+w[v]+dis[u]+dis[v]-(dis[LCA(u,v)]<<1);}
}
namespace tree2{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int top[N],dep[N],fa[N],sz[N],son[N],low[N],dfn[N],cnt;ll dis[N];
void dfs1(int u){
sz[u]=1,dep[u]=dep[fa[u]]+1,dfn[u]=++cnt;
go(u)if(v!=fa[u]){
dis[v]=dis[u]+e[i].w,fa[v]=u,dfs1(v),sz[u]+=sz[v];
sz[v]>sz[son[u]]?son[u]=v:0;
}
low[u]=cnt;
}
void dfs2(int u,int t){
top[u]=t;if(!son[u])return;
dfs2(son[u],t);
go(u)!top[v]?(dfs2(v,v),0):0;
}
inline int LCA(R int u,R int v){
while(top[u]!=top[v]){
dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
inline void Pre(){fp(i,1,n)head[i]=0;tot=0;}
struct node{
int u,v;ll d;
node():u(0),v(0),d(0){}
node(R int uu,R int vv,R ll dd):u(uu),v(vv),d(dd){}
node(R int uu,R int vv):u(uu),v(vv){d=tree3::Dis(u,v);}
inline bool operator <(const node &b)const{return !u?1:!b.u?0:d<b.d;}
inline node operator +(const node &b)const{
node res=max(*this,b);
cmax(res,max(node(u,b.u),node(u,b.v))),
cmax(res,max(node(v,b.u),node(v,b.v)));
return res;
}
inline ll merge(const node &b){
return max(max(node(u,b.u),node(u,b.v)),max(node(v,b.u),node(v,b.v))).d;
}
}f[N][2];
int q[N<<2],stack[N<<1],t;bool vis[N],ins[N];ll tag;
inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
void dp(int u){
f[u][0]=f[u][1]=node();
vis[u]?(f[u][ty[u]]=node(u,u,0),0):0;
go(u){
dp(v),
cmax(res,max(f[u][0].merge(f[v][1]),f[u][1].merge(f[v][0]))+tag-(dis[u]<<1)),
f[u][0]=f[u][0]+f[v][0],
f[u][1]=f[u][1]+f[v][1];
}
}
void solve(ll val){
tag=val,t=0,tot=0;
fp(i,1,::top[0])q[++t]=s[0][i];
fp(i,1,::top[1])q[++t]=s[1][i];
sort(q+1,q+1+t,cmp);
fp(i,1,t)vis[q[i]]=ins[q[i]]=1,w[q[i]]+=dis[q[i]];
for(R int i=t,lca;i>1;--i)lca=LCA(q[i],q[i-1]),!ins[lca]?(q[++t]=lca,ins[lca]=1):0;
sort(q+1,q+1+t,cmp);
for(R int i=1,top=0;i<=t;++i){
while(top&&low[stack[top]]<dfn[q[i]])--top;
if(top)add(stack[top],q[i],dis[q[i]]-dis[stack[top]]);
stack[++top]=q[i];
}
dp(q[1]);
fp(i,1,t)vis[q[i]]?w[q[i]]-=dis[q[i]]:0,vis[q[i]]=ins[q[i]]=head[q[i]]=0;
}
}
namespace tree1{
struct eg{int v,nx;ll w;}e[N<<2];int head[N<<1],tot=1;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int cnt;
namespace qwq{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],las[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
void ins(int u,int v,ll w){
++cnt,tree1::add(cnt,v,w),tree1::add(v,cnt,w),
tree1::add(las[u],cnt,0),tree1::add(cnt,las[u],0),
las[u]=cnt;
}
void build(int u,int fa){go(u)if(v!=fa)ins(u,v,e[i].w),build(v,u);}
inline void init(){fp(i,1,n)las[i]=i;cnt=n;build(1,0);}
}
bool vis[N];int sz[N],rt,mx;ll dis[N];
void findrt(int u,int fa,int size){
sz[u]=1;
go(u)if(!vis[i>>1]&&v!=fa){
findrt(v,u,size),sz[u]+=sz[v];
cmin(mx,max(sz[v],size-sz[v]))?rt=i:0;
}
}
void dfs(int u,int fa,int op){
u<=n?ty[u]=op,s[op][++top[op]]=u:0;
go(u)if(v!=fa&&!vis[i>>1])
dis[v]=dis[u]+e[i].w,dfs(v,u,op);
}
void solve(int u,int size){
mx=1e9,findrt(u,0,size);
if(mx==1e9)return;
vis[rt>>1]=1;
int nw=rt,ss=size-sz[e[rt].v];
top[0]=top[1]=dis[e[rt].v]=dis[e[rt^1].v]=0;
dfs(e[rt].v,0,1),dfs(e[rt^1].v,0,0);
fp(i,1,top[0])w[s[0][i]]+=dis[s[0][i]];
fp(i,1,top[1])w[s[1][i]]+=dis[s[1][i]];
tree2::solve(e[rt].w);
fp(i,1,top[0])w[s[0][i]]-=dis[s[0][i]];
fp(i,1,top[1])w[s[1][i]]-=dis[s[1][i]];
solve(e[nw].v,sz[e[nw].v]),solve(e[nw^1].v,ss);
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=read();
fp(i,2,n+n)lg[i]=lg[i>>1]+1;
int u,v;ll w;
fp(i,1,n-1)u=read(),v=read(),w=read(),tree1::qwq::add(u,v,w),tree1::qwq::add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),tree2::add(u,v,w),tree2::add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),tree3::add(u,v,w),tree3::add(v,u,w);
tree2::dfs1(1),tree2::dfs2(1,1),tree2::Pre(),
tree3::dfs1(1),tree3::dfs2(1,1);
tree1::qwq::init(),tree1::solve(1,tree1::cnt);
printf("%lld\n",res);
return 0;
}
洛谷P4220 [WC2018]通道(边分治+虚树)的更多相关文章
- [WC2018]通道——边分治+虚树+树形DP
题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...
- LOJ 2339 「WC2018」通道——边分治+虚树
题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...
- UOJ#347. 【WC2018】通道 边分治 虚树
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ347.html 题意 有三棵树,边有边权. 对于所有点对 (x,y) 求在三棵树上 x 到 y 的距离之和 ...
- 洛谷4103 HEOI2014大工程(虚树+dp)
又是一道虚树好题啊 我们建出来虚树,然后考虑dp过程,我们分别令\(sum[x],mndis[x],mxdis[x],size[x]\)为子树内的路径长度和,最短链,最长链,子树内关键点个数. 对于一 ...
- 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)
题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...
- 【UOJ347】【WC2018】通道 边分治 虚树 DP
题目大意 给你三棵树,点数都是\(n\).求 \[ \max_{i,j}d_1(i,j)+d_2(i,j)+d_3(i,j) \] 其中\(d_k(i,j)\)是在第\(k\)棵数中\(i,j\)两点 ...
- 洛谷 P3377 【模板】左偏树(可并堆)
洛谷 P3377 [模板]左偏树(可并堆) 题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或 ...
- 洛谷P3377 【模板】左偏树(可并堆) 题解
作者:zifeiy 标签:左偏树 这篇随笔需要你在之前掌握 堆 和 二叉树 的相关知识点. 堆支持在 \(O(\log n)\) 的时间内进行插入元素.查询最值和删除最值的操作.在这里,如果最值是最小 ...
- 洛谷 P6199 - [EER1]河童重工(点分治+虚树)
洛谷题面传送门 神仙题. 首先看到这样两棵树的题目,我们肯定会往动态树分治的方向考虑.考虑每次找出 \(T_2\) 的重心进行点分治.然后考虑跨过分治中心的点对之间的连边情况.由于连边边权与两棵树都有 ...
随机推荐
- centos7 时间修改
转子 http://blog.csdn.net/kuluzs/article/details/52825331 在CentOS 6版本,时间设置有date.hwclock命令,从CentOS 7开始, ...
- pyinstaller打包后运行提示找不到模块
截止到2017年9月20号,pyinstaller只支持到python3.5,如果需要支持到3.6,需要自己在github上下载pyinstaller的开发版. 在打包时候,并没有提示错误,可以顺利打 ...
- Excel VBA 获取按钮对象
今天给同事写了两个VBA宏,并分别把宏赋给了两个按钮. 因为两个宏都是实现在两种显示方式之间切换,于是我想除了功能的实现外,还希望在切换到其中一种方式时,按钮上面的文字也可以跟着改变,起到提示作用. ...
- 利用fetch进行POST传参
fetch(config.host+"url",{ method:"POST", mode: 'cors',跨域请求 headers: { ...
- Python实现常见算法[3]——汉罗塔递归
#!/usr/bin/python # define three list var. z1 = [1,2,3,4,5,6,7,"1st zhu"] z2 = ["2st ...
- Maven01 环境准备、maven项目结构、编译/测试/打包/清除、安装、
0 前提准备 0.1 安装java开发环境 0.2 安装maven工具 1 maven项目基本结构 如图所示,整个maven项目有业务文件.测试文件.POM依赖管理文件:其实还有一个资源文件resou ...
- rsyslog收集nginx日志配置
rsyslog日志收集配置 rsyslog服务器收集各服务器的日志,并汇总,再由logstash处理 请查看上一篇文章 http://bbotte.blog.51cto.com/6205307/16 ...
- Linux环境(Centos) 安装mysql
MariaDB是mysql的开源分支,自从mysql被oracle收购商业化之后,mysql之父在mysql5.5开源的版本的基础上重新开了一个分支,centos也把MariaDB作为mysql的默认 ...
- appium desktop v1.2.7在android7.0上运行报错“Error: Error executing adbExec”
1.参考下面链接,https://stackoverflow.com/questions/42283921/unable-to-run-appium-tests-on-android-7-0:得知需要 ...
- Ubuntu14.04下FTP服务器的搭建配置 标签: ubuntuftp服务器虚拟机 2017-06-13 15:24 161人阅读 评
首先说明一下,我是在虚拟机中装的Ubuntu14.04,物理机是Win10,最初只是为了在两个系统间传输文件才在Ubuntu中安装了ftp服务器,从Windows端登陆其即可.最初也是按照网上的各种教 ...