Description
给出一棵 n 个结点的树,点 i 有正权值 wiw_iwi,wiw_iwi 互不相同。
除了权值最小的点,保证每个点 u 都有一个邻点 v 使得 wvw_vwv < wuw_uwu。
构造一棵树,最小化代价:
- 对于每个点 u,产生 degu×wudeg _u \times w_udegu×wu 的代价,其中 degudeg _udegu 是构造的树中 u 的度数。
- 对于每条边 (u, v),产生 ⌈log2dis(u,v)⌉×min(wu,wv)⌈log_2dis(u, v)⌉ \times min(w_u, w_v)⌈log2dis(u,v)⌉×min(wu,wv) 的代价,其中dis(u, v) 是给出的树中 u, v 的距离。
n ≤ 5×1055 \times 10^55×105
Solution
- 首先要会利用题目中给的 wv<wuw_v<w_uwv<wu 的性质,可以发现这个性质在树上加上只有一个最小值的条件是可以得到这个结论的:除最小值外,每个点都有且仅有一个与他相邻的点 v 权值小于它。
因为对于点 u ,如果有一个点 v 小于它,那么就要需要另外一个点去小于 v ,那这种依赖只会在最小值的时候停止,但是整棵树只有一个最小值,所以每个点也只能有且仅有一个点小于它。 - 于是题目那个描述诡异的限制变成了:以权值最小的点为根,每个点的权值均大于父亲权值。
- 把点的代价放进边:(u, v) 代价为(⌈log2dis(u,v)⌉+1)∗min(wu,wv)+max(wu,wv)(⌈log_2dis(u, v)⌉ + 1) ∗ min(w_u, w_v) + max(w_u, w_v)(⌈log2dis(u,v)⌉+1)∗min(wu,wv)+max(wu,wv)
- 按照 kruskal,对于任意一点 u,与 u 相连的最小权值的边 (u, v) 一定在 MST 中。
- 枚举每个点,找与其相连的最小权值的边:
-
如果一个点 x 选中了它子树中的某个点相连,那么它还不如选择权值更小的祖先,代价更小。
-
如果一个点 x 选中了深度小于它的非祖先节点,那么它还不如从它选择的这个节点往上走,走到一个离它最近的,是 x 的祖先的节点,因为这个新节点肯定比原来选择的节点距离 x 更近,且权值更小,所以显然更优。
- 因此对于除根结点外的所有点 u,v 一定是它的 2k2^k2k 级祖先或者根结点。倍增求解。
- 这些边恰好组成 MST,直接枚举找到它们。
Code
#include<iostream>
#include<cstdio>
using namespace std;
const int N=5e5+5;
const long long inf=1e18;
struct Edge{int v,nxt;
}edge[N<<1];
int n,head[N],cnt,w[N],rt,fa[N][22],dep[N];
long long res,ans;
void add(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u){if(u!=rt){int j;for(j=1;j<=20;j++){if(dep[u]<(1<<j)) break;fa[u][j]=fa[fa[u][j-1]][j-1];}res=inf;for(j=0;j<=20;j++){if(dep[u]<(1<<j)) break;res=min(res,1ll*(j+1)*w[fa[u][j]]+w[u]);}if(dep[u]!=(1<<j))res=min(res,1ll*(j+1)*w[rt]+w[u]);ans+=res;}for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u][0]) continue;fa[v][0]=u;dep[v]=dep[u]+1;dfs(v);}
}
int main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&w[i]);if(!rt) rt=i;else if(w[rt]>w[i]) rt=i;}for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);}dfs(rt);printf("%lld\n",ans);return 0;
}
参考文章:
https://www.luogu.com.cn/blog/qlwpc/cf1088f-ti-xie