1、并查集
可以并查集:
考虑对点权的限制。
尝试逐点枚举点权,向点权大于等于自己的节点扩展,计算最大路径。
优化:瓶颈在于还是有很多重复的。
上述的每个节点扩展后形成的连通块点集成为一个集合,
从大点权到小点权只要集合拓展。
维护集合:考虑并查集
点权排序,维护集合内最长链即可。
nlog
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> #define F(i,a,b) for(rg int i=a;i<=b;++i) #define rg register #define LL long long #define il inline #define pf(a) printf("%d ",a) #define phn puts("") using namespace std; #define int LL int read(); /* 可以并查集: 考虑对点权的限制。 尝试逐点枚举点权,向点权大于等于自己的节点扩展,计算最大路径。 优化:瓶颈在于还是有很多重复的。 上述的每个节点扩展后形成的连通块点集成为一个集合, 从大点权到小点权只要集合拓展。 维护集合:考虑并查集 点权排序,维护集合内最长链即可。 */ #define N 100010 int n; int to[N<<1],fir[N<<1],len[N<<1],head[N],cnt; int val[N]; int f[N][20],s[N],d[N]; il int max(int x,int y){return x>y?x:y;} il void add(int x,int y,int w){to[++cnt]=y;fir[cnt]=head[x];head[x]=cnt;len[cnt]=w;} void dfs(int x,int fa){ f[x][0]=fa;d[x]=d[fa]+1; F(i,1,18)f[x][i]=f[f[x][i-1]][i-1]; for(int i=head[x];i;i=fir[i]){ int v=to[i]; if(v!=fa)s[v]=s[x]+len[i],dfs(v,x); } } il int lca(int x,int y){ if(d[x]<d[y])swap(x,y); for(int i=18;~i;--i)if(d[f[x][i]]>=d[y])x=f[x][i]; if(x==y)return x; for(int i=18;~i;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } struct node{ int x,w; friend bool operator < (node a,node b){return a.w>b.w;} }a[N]; int jh[N],w[N],dl[N],dr[N]; bool vis[N]; LL ans; int find(int x){return jh[x]==x?x:jh[x]=find(jh[x]);} il int hb(int x,int y){ x=find(x),y=find(y); if(x==y)return x; int a[2]={dl[x],dr[x]},b[2]={dl[y],dr[y]}; int mxd=w[y],l=dl[y],r=dr[y]; F(i,0,1){ F(j,0,1){ int dis=s[a[i]]+s[b[j]]-2ll*s[lca(a[i],b[j])]; if(dis>mxd){ mxd=dis;l=a[i];r=b[j]; } } } if(mxd>w[x]){ w[x]=mxd;dl[x]=l;dr[x]=r; } jh[y]=x; return x; } signed main(){ // freopen("b.in","r",stdin); int T=read(); while(T--){ cnt=0;memset(head,0,sizeof(head));ans=0; n=read(); F(i,1,n)a[i]=(node){i,read()}; for(rg int i=2,u,v,val;i<=n;++i) u=read(),v=read(),val=read(),add(u,v,val),add(v,u,val); dfs(1,0); sort(a+1,a+n+1); ans=0; F(i,1,n){ jh[i]=i;w[i]=vis[i]=0;dl[i]=dr[i]=i; } rg int u; F(i,1,n){ u=a[i].x;vis[u]=1; for(rg int j=head[u];j;j=fir[j]){ int v=to[j]; if(vis[v]){ int rt=hb(u,v); ans=max(ans,w[rt]*a[i].w); } } } printf("%lld\n",ans);// } } il int read(){ rg int s=0;rg char ch; while(ch=getchar(),!isdigit(ch)); for(;isdigit(ch);s=s*10+(ch^48),ch=getchar()); return s; } /* g++ 1.cpp -g time ./a.out 1 3 1 2 3 1 2 1 1 3 2 */
2、点分治。
也是对上述暴力的优化。
nlog*log,点分×set (第二个log是log当前点儿子。常数小。)
维护当前分治子树内各点的到根距离、min点权。
然后按最小点权大到小sort记录的信息,更新max边,用当前点×max边。
为了避免来自同一子树,要记录该点所属的根的儿子是谁。
multiset维护各儿子当前max边。
wmz用了线段树做set的功能。但是没必要,增加了常数和码量。不如multiset。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define lch p<<1 #define rch p<<1|1 using namespace std; const int N=1e5+7; const int inf=0x7f7f7f7f; struct node{ long long mx; bool clear; }s[N<<2]; inline void down(int p){ if(!s[p].clear) return ; s[lch].clear=s[rch].clear=1; s[lch].mx=s[rch].mx=0; s[p].clear=0; } inline void up(int p){ s[p].mx=max(s[lch].mx,s[rch].mx); } void insert(int p,int l,int r,int pos,long long val){ if(l==r) return (void)(s[p].mx=max(s[p].mx,val)); down(p); int mid=l+r>>1; if(pos<=mid) insert(lch,l,mid,pos,val); else insert(rch,mid+1,r,pos,val); up(p); } long long query(int p,int l,int r,int L,int R){ if(l>=L&&r<=R) return s[p].mx; down(p); int mid=l+r>>1; long long ans=0; if(L<=mid) ans=max(query(lch,l,mid,L,R),ans); if(R>mid) ans=max(query(rch,mid+1,r,L,R),ans); up(p); return ans; } struct data{ int mn,k; long long sum; data(){} data(int mn,long long sum,int k):mn(mn),sum(sum),k(k){} inline friend bool operator < (const data &a,const data &b){ return a.mn>b.mn; } }que[N]; int n,tot,root,sumsz,mn,cnt; bool v[N]; int head[N],nxt[N<<1],to[N<<1],w[N<<1],d[N],sz[N]; long long ans; void calc(int x,int from,int mn,long long sum,int k){ mn=min(mn,d[x]); ans=max(ans,sum*mn); que[++cnt]=data(mn,sum,k); for(int i=head[x];i;i=nxt[i]) if(!v[to[i]]&&to[i]!=from) calc(to[i],x,mn,sum+w[i],k); } void findroot(int x,int from,int mxp=0){ sz[x]=1; for(int i=head[x];i;i=nxt[i]){ if(to[i]==from||v[to[i]]) continue; findroot(to[i],x); sz[x]+=sz[to[i]]; if(sz[to[i]]>mxp) mxp=sz[to[i]]; } if(sumsz-sz[x]>mxp) mxp=sumsz-sz[x]; if(mxp<mn) mn=mxp,root=x; } void solve(int x,int num=0){ v[x]=1; cnt=0; for(int i=head[x];i;i=nxt[i]) if(!v[to[i]]) calc(to[i],x,d[x],w[i],++num); sort(que+1,que+cnt+1); for(int i=1;i<=cnt;++i){ if(i!=1){ long long sum=0; if(que[i].k!=1) sum=max(sum,query(1,1,num,1,que[i].k-1)); if(que[i].k!=num) sum=max(sum,query(1,1,num,que[i].k+1,num)); ans=max(ans,que[i].mn*(sum+que[i].sum)); } insert(1,1,num,que[i].k,que[i].sum); } s[1].clear=1; for(int i=head[x];i;i=nxt[i]){ if(v[to[i]]) continue; sumsz=sz[to[i]]; mn=N; findroot(to[i],x); solve(root); } } inline void add(int a,int b,int val){ nxt[++tot]=head[a]; head[a]=tot; to[tot]=b; w[tot]=val; } inline int read(register int x=0,register char ch=getchar(),bool f=0){ while(!isdigit(ch)) f=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int main(){ //freopen("b.in","r",stdin); int T=read(); while(T--){ n=read(); ans=0; tot=0; memset(head,0,sizeof(head)); memset(v,0,sizeof(v)); for(int i=1;i<=n;++i) d[i]=read(); for(int i=1,a,b,val;i<n;++i){ a=read(); b=read(); val=read(); add(a,b,val); add(b,a,val); } findroot(1,1); solve(1); printf("%lld\n",ans); } return 0; } /* g++ bf.cpp -g ./a.out */