有一个 n n n个结点的树,根为 1 1 1。
对于 m m m次询问,每次给定 k k k个关键点,需要选定一些边断开(每条边有断开的代价),使得 1 1 1号点和 k k k个点中的任意结点都不连通。求满足要求的最小代价。
保证给定的 k k k个关键点中一定不包含 1 1 1号点
2 ≤ n ≤ 2.5 × 1 0 5 , 1 ≤ m ≤ 5 × 1 0 5 , Σ k ≤ 5 × 1 0 5 2\le n\le 2.5\times10^5,1\le m\le5\times10^5,\Sigma k\le 5\times10^5 2≤n≤2.5×105,1≤m≤5×105,Σk≤5×105
注: Σ k \Sigma k Σk即为所有 k k k之和
朴素想法树形DP,在此略过
如何优化?
我们可以发现有许多点是无用的
注:没有
4
4
4,请不要在意
如这棵树,若
8
,
10
,
12
,
16
,
17
8,10,12,16,17
8,10,12,16,17是关键点,那么只有如下的点和边有用
显然简化了不少。
那如何构造这样一颗树呢?
- 所有关键点以dfn排序
- 两两求出LCA
- 将所有LCA和点1插入虚树的点数组,然后按dfn排序后去重
- 再次两两求LCA,将两个点中dfn较大的点与求出来的LCA连线
做完了
Code:
bool cmp(int x, int y) {//按照 dfn 序排序
return dfn[x] < dfn[y];
}
void build() {
int len=0;
sort(h + 1, h + k + 1, cmp); //输入的关键点
for(int i = 1; i < k; i++) {
a[++len] = h[i];
a[++len] = lca(h[i], h[i + 1]); //插入 lca
}
a[++len] = h[k]; a[++len] = 1;
sort(a + 1, a + len + 1, cmp); //把所有虚树上的点按照 dfn 序排序
len = unique(a + 1, a + len + 1) - a - 1; //去重
for (int i = 1, lc; i < len; i++) {
lc = lca(a[i], a[i + 1]);
add(lc, a[i + 1]); //连边
}
}
注:不是我写的,但是有错还是找我的洛谷账号 @FeS2_qwq(或直接联系@babyec 虽然我很不建议你这样干)
但是,别忘了我们还有边权!!!新边权怎么办?
别着急,我们有只用老边权处理新图的方案。
设 m i n w i minw_i minwi为 1 1 1到 i i i之间的最短边权,若 i i i为关键点,则必须与点 1 1 1断开 d p i = m i n w i dp_i=minw_i dpi=minwi
否则,则 i i i既可以和点 1 1 1断开,也可以和下面的关键点断开 d p i = min ( m i n w i , Σ d p j ) dp_i=\min(minw_i,\Sigma dp_j) dpi=min(minwi,Σdpj)
注: Σ d p j \Sigma dp_j Σdpj就是所有 i i i的儿子的 d p dp dp值之和
哦,漏了一点,时间复杂度 O ( Σ k log n ) O(\Sigma k\log n) O(Σklogn),大概算了一下,上面的问题可以 ≤ 90 ms \le90\operatorname{ms} ≤90ms的时间完成(?
然后大概就做完了吧。
纯纯的模板啊!!!
宣传:关注洛谷账号@FeS2_qwq喵,关注洛谷账号@FeS2_qwq谢谢喵
该文同步发表于