博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj2870最长道路tree——边分治
阅读量:5874 次
发布时间:2019-06-19

本文共 2641 字,大约阅读时间需要 8 分钟。

简化版描述:

给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
其中链长度定义为链上点的个数。
 

有几个不同的做法:

1.sort+并查集+树的直径。边从大到小加入,并查集维护连通块,记录连通块的直径的两个端点,合并连通块的时候更新直径,并且用len*bian[i].w更新答案

  有排序,O(nlogn)

2.点分治+树状数组。点分治路径合并的时候挺恶心。先都扫一遍所有子树,把路径最小值作为下标,链长作为权值放进树状数组里。

  再枚举子树搜一遍,先减去当前子树的贡献,再搜的时候从树状数组找后缀最大值。思想就是钦定最小值在当前子树的某个路径中

  O(nlog^2)

3.点分治

  把过重心的子树分成两堆递归?不懂。。。O(nlogn)

  代码太长,还不如写边分治

4.边分治

 本身其实点分治还可以暴力枚举重心子树的所有兄弟然后双指针,从而不用树状数组,但是复杂度是O(度数^2nlogn)的。不优秀

边分治就两个儿子自然好办啦

三度化然后边分治,两个子树的路径存进两个数组,按照最小值sort,然后倒序枚举双指针。记录最小值不小于当前钦定子树的最大的深度,左右各做一遍即可

过虚点其实一定过了x,所以虚点权值定为x的权值。

虚边的权值就是0,实边是1,两点链长是深度+1

 

代码:

注意,如果对面的儿子没有选择一个点,那么不能计算当前的中心边的权值

 

#include
#define reg register int#define il inline#define mk(x,y) make_pair(x,y)#define fi first#define se second#define numb (ch^'0')using namespace std;typedef long long ll;il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x);}namespace Miracle{const int N=200000+5;const int inf=0x3f3f3f3f;int n;int tot;int val[N];struct node{ int nxt,to; int w;}e[2*N],bian[2*N];int cnt1=1,cnt2;int hd[N],pre[N];ll ans;void add(int x,int y,int z){ e[++cnt1].nxt=hd[x]; e[cnt1].to=y; e[cnt1].w=z; hd[x]=cnt1;}void add_c(int x,int y){ bian[++cnt2].nxt=pre[x]; bian[cnt2].to=y; pre[x]=cnt2; }void rebuild(int x,int fa){ int ff=0; for(reg i=pre[x];i;i=bian[i].nxt){ int y=bian[i].to; if(y==fa) continue; if(!ff){ add(x,y,1); add(y,x,1); ff=x; }else{ int tmp=++tot; val[tmp]=val[x]; add(ff,tmp,0);add(tmp,ff,0); add(tmp,y,1);add(y,tmp,1); ff=tmp; } rebuild(y,x); }}pair
ls[N],rs[N];int lsc,rsc;bool cmp(pair
A,pair
B){ if(A.fi==B.fi) return A.se
=1;--i){ while(ptr&&rs[ptr].fi>=ls[i].fi){ mxdep=max(mxdep,rs[ptr].se);--ptr; } ans=max(ans,(ll)((ll)mxdep+e[edge].w+ls[i].se+1)*ls[i].fi); } mxdep=0;ptr=lsc; for(reg i=rsc;i>=1;--i){ while(ptr&&ls[ptr].fi>=rs[i].fi){ mxdep=max(mxdep,ls[ptr].se);--ptr; } ans=max(ans,(ll)((ll)mxdep+e[edge].w+rs[i].se+1)*rs[i].fi); } int szrt1=totsz-sz[rt2]; int szrt2=sz[rt2]; int tmprt1=rt1,tmprt2=rt2; totsz=szrt1; divi(tmprt1,x); totsz=szrt2; divi(tmprt2,x);}int main(){ rd(n); for(reg i=1;i<=n;++i) rd(val[i]),ans=max(ans,(ll)val[i]); int x,y; for(reg i=1;i

 

转载于:https://www.cnblogs.com/Miracevin/p/10430192.html

你可能感兴趣的文章
Ruby的case语句
查看>>
Linux的链接文件-ln命令
查看>>
maven的tomcat插件如何进行debug调试
查看>>
table表头固定
查看>>
截取字符串中两个字符串中的字符串
查看>>
spring xml properties split with comma for list
查看>>
判断点是否在三角形内
查看>>
Android实战简易教程-第二十三枪(基于Baas的用户注冊验证username是否反复功能!)...
查看>>
在odl中怎样实现rpc
查看>>
leetcode 110 Balanced Binary Tree
查看>>
python活用isdigit方法显示系统进程
查看>>
项目开发总结
查看>>
知行合一
查看>>
jmeter插件之jsonpath提取响应结果和做断言
查看>>
发布支持多线程的PowerShell模块 —— MultiThreadTaskRunner
查看>>
Ubuntu ctrl+alt会导致窗口还原的问题
查看>>
第四十期百度技术沙龙笔记整理
查看>>
推荐系统那点事 —— 基于Spark MLlib的特征选择
查看>>
linux 下RTL8723/RTL8188调试记录(命令行)【转】
查看>>
開始新的征程
查看>>