简化版描述:
给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
其中链长度定义为链上点的个数。
有几个不同的做法:
1.sort+并查集+树的直径。边从大到小加入,并查集维护连通块,记录连通块的直径的两个端点,合并连通块的时候更新直径,并且用len*bian[i].w更新答案
有排序,O(nlogn)
2.点分治+树状数组。点分治路径合并的时候挺恶心。先都扫一遍所有子树,把路径最小值作为下标,链长作为权值放进树状数组里。
再枚举子树搜一遍,先减去当前子树的贡献,再搜的时候从树状数组找后缀最大值。思想就是钦定最小值在当前子树的某个路径中
O(nlog^2)
3.点分治
把过重心的子树分成两堆递归?不懂。。。O(nlogn)
代码太长,还不如写边分治
4.边分治
https://blog.csdn.net/litble/article/details/80853633
本身其实点分治还可以暴力枚举重心子树的所有兄弟然后双指针,从而不用树状数组,但是复杂度是O(度数^2nlogn)的。不优秀
边分治就两个儿子自然好办啦
三度化然后边分治,两个子树的路径存进两个数组,按照最小值sort,然后倒序枚举双指针。记录最小值不小于当前钦定子树的最大的深度,左右各做一遍即可
过虚点其实一定过了x,所以虚点权值定为x的权值。
虚边的权值就是0,实边是1,两点链长是深度+1
代码:
注意,如果对面的儿子没有选择一个点,那么不能计算当前的中心边的权值
#include<bits/stdc++.h> #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<int,int>ls[N],rs[N]; int lsc,rsc; bool cmp(pair<int,int>A,pair<int,int>B){ if(A.fi==B.fi) return A.se<B.se; return A.fi<B.fi; } int totsz; int rt1,rt2,edge; bool vis[2*N]; int sz[N],mx; void dfs1(int x,int fa){ sz[x]=1; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa) continue; if(vis[i]) continue; dfs1(y,x); int now=max(sz[y],totsz-sz[y]); if(now<mx) { mx=now;rt1=x;rt2=y;edge=i; } sz[x]+=sz[y]; } } void dfs2(int x,int fa,int mi,int dep,int typ){ if(typ==1){ ls[++lsc]=mk(mi,dep); }else{ rs[++rsc]=mk(mi,dep); } for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa||vis[i]) continue; dfs2(y,x,min(mi,val[y]),dep+e[i].w,typ); } } void divi(int x,int from ){ //cout<<" divi "<<x<<" "<<totsz<<" from "<<from<<endl; if(totsz==1) return; rt1=rt2=edge=0; mx=inf; dfs1(x,0); //cout<<" rt1 "<<rt1<<" rt2 "<<rt2<<" sz "<<sz[rt1]<<" "<<sz[rt2]<<endl; vis[edge]=vis[edge^1]=1; lsc=rsc=0; dfs2(rt1,0,val[rt1],0,0); dfs2(rt2,0,val[rt2],0,1); sort(ls+1,ls+lsc+1,cmp); sort(rs+1,rs+rsc+1,cmp); int mxdep=0; int ptr=rsc; for(reg i=lsc;i>=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<n;++i){ rd(x);rd(y); add_c(x,y);add_c(y,x); } tot=n; rebuild(1,0); totsz=tot; //cout<<" tot "<<tot<<endl; divi(1,0); printf("%lld",ans); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/2/25 9:29:02 */