您的位置:首页 > 其它

2017多校联合第三场 1005题 hdu 6060 RXD and dividing (超详细!!!)构造

2017-08-02 00:00 330 查看
题目链接

题意:

给定一棵 n 个节点的树,1 为根。现要将节点 2 ~ n 划分为 k 块,使得每一块与 根节点 形成的最小斯坦纳树的 边权值 总和最大。

看了题解之后的思考:

题解是:记有向边 (u, v) 长度为 w[v],以点 v 为根的子树的节点总数为 sz[v],那么答案就是 w[v] * min(sz[v], k) 对每个点求个和。

题解的说法是:可以通过构造使得 balabala...

http://bestcoder.hdu.edu.cn/blog/2017-multi-university-training-contest-3-solutions-by-%E6%B4%AA%E5%8D%8E%E6%95%A6/

一开始觉得...这简直是在开玩笑吧。后来仔细画画图,好像真是这么回事。原来之前思考的时候一直陷入了一个误区,至于是什么,我们之后再说。

现在,先来谈一谈,究竟如何构造。

首先,入手点是 每条边 被算了多少次,即每条边的权值对最终答案的贡献。(这种看贡献的想法真的很重要(敲黑板))

其次,从题解中我们可以发现,这个算了多少次 好像就 简简单单地 取了 可能取到的最大值。

然而,这个尽可能的能被允许的最大值 min(sz[v], k) 能保证在每个点都一定能取到吗?

答案是可以的。

首先,要明确的是,对于以 v 为根的子树而言,将其划分的块数越多,对 w[v] 的贡献就越大。

下面开始分类讨论。

1. 

当除根以外所有节点的 sz 值 都 <= k 时,对于任意一个点 v,可将以 v 为根的子树划分为 sz 块,此时贡献最大。

对于任意两个点 v1,v2 而言:

1)若 v2 在 以 v1 为根的子树内(v1 在以 v2 为根的子树内 同理),可将 v1 看成一个整体,其中 sz1 个点划分为 sz1 块即可

2)若 v2 不在以 v1 为根的子树内,且 v1 不在以 v2 为根的子树内,那么 v1 的 sz1 块 与 v2 的 sz2 块就是完全独立的,可以简单地将 sz1 中的任一个集合 与 sz2 中的任一个集合合并,只要保证 sz1 中不同的集合 不与 sz2 中的同一个集合合并, sz2 中不同的集合 也不与 sz1 中的同一个集合合并 即可(其他合并方式也可以,如使 其中一块 自成一个集合等等,只要满足前述条件 并且 合并后的总数 <= k 即可)

反复进行上述操作,最终可得到 sz (sz <= k) 块,并且其中每一步都取了尽可能的最大值

2. 

存在节点 u,其 sz 值 > k,

对于 u 节点与另外一点 v 而言:

1)若 v 在以 u 为根的子树中,显然,u 必有 sz 值 <= k 的孩子节点,若 v 不是,则可递归地再往下找。

不妨就设其为 v,则 v 的 sz 值 <= k,并且已经被划分好为 szv 块。那么,对于 u 的其他没有被划分的孩子节点,只需将 szu - szv 个节点划分为 k - szv 块即可。

2)若 v 不在以 u 为根的子树中,u 也不在以 v 为根的子树中,合并方式同 1. 2) 相同

反复进行上述操作,最终可得到 k 块,并且其中每一步都取了尽可能的最大值

综上,可以构造出这样的集合使得答案取到最大值。

反思:

一开始思维局限在了每一个子树内要划分出一个集合,压根没想到可以把(相对)不相干的点塞到同一个集合中,主要还是受没改题面之前要求 最小值 的影响...

再提一提最小值怎么做吧,学姐给出的做法是,将根节点到其余每个节点的距离从小到大排个序(包括1到1的距离0),要注意的是,节点有几个子树就把这段距离算几遍,用bfs+贪心(优先队列),取前 k 个最小的,然后加上树上本身每条边的权值,即为答案。

后话:

看了题解后写了一个 dfs,结果用测试数据测结果卡了...一狠心在 hdu 上交了一遍竟然 AC 了。跑去看了 std,回来乖乖地又写了一遍 bfs,然后测试数据就也能跑通了...真是......厉害了。第一场的 12 题也是这样,总都是搞得深度很深来卡递归,也是...没办法了Orz

(后面的代码上面 bfs 和 dfs 都有)

AC代码如下:

#include <bits/stdc++.h>
#define maxn 1000010
int ne[maxn], sz[maxn], w[maxn], n, k, tot, q[maxn], fa[maxn];
bool vis[maxn];
inline min(int a, int b) { return a < b ? a : b; }
typedef long long LL;
struct Edge {
int to, dist, ne;
Edge(int a = 0, int b = 0, int c = 0) : to(a), dist(b), ne(c) {}
}edge[maxn * 2];
void add(int x, int y, int d) {
edge[tot] = Edge(y, d, ne[x]);
ne[x] = tot++;
}
void dfs(int u, int fa) {
sz[u] = 1;
for (int i = ne[u]; i != -1; i = edge[i].ne) {
Edge e = edge[i]; int v = e.to;
if (v == fa) continue;
w[v] = e.dist;
dfs(v, u);
sz[u] += sz[v];
}
}
void bfs(int src) {
memset(vis, 0, sizeof(vis));
int f = 0, r = 0;
q[r++] = src; vis[src] = true;
while (r > f) {
int u = q[f]; sz[u] = 1;
++f;
for (int i = ne[u]; i != -1; i = edge[i].ne) {
Edge e = edge[i]; int v = e.to;
if (vis[v]) continue;
q[r++] = v; vis[v] = true;
fa[v] = u; w[v] = e.dist;
}
}
for (int i = r - 1; i >= 0; --i) sz[fa[q[i]]] += sz[q[i]];
}
void work() {
memset(ne, -1, sizeof(ne));
tot = 0;
for (int i = 1; i < n; ++i) {
int x, y, d;
scanf("%d%d%d", &x, &y, &d);
add(x, y, d);
add(y, x, d);
}
//    dfs(1, -1);
bfs(1);
LL ans = 0;
for (int i = 2; i <= n; ++i) {
ans += (LL)w[i] * min(sz[i], k);
}
printf("%lld\n", ans);
}
int main() {
while (scanf("%d%d", &n, &k) != EOF) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: