[BOI2003]Gem 气垫车

本题传送门

题目大意

给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数。

唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。

解题思路

本题相当于一个染色问题。

本蒟蒻开始以为是 1212 染色。(危

其实可以有一组 Hack 数据。

数据1

将数据构造成一棵树:

在这里插入图片描述

如果是 1212 染色,则总价值最小的染色情况为:

在这里插入图片描述

如图可知,总价值为 2+1+1+1+1+2+2+2=122+1+1+1+1+2+2+2=12,与输出不符,故不能用 1212 染色。

再根据图进行分析,可以发现,对总价值贡献最多的是节点 5533 个子节点,贡献为 2×3=62 \times 3=6

那能不能修改通过修改图的一个节点,使得这 33 个节点的贡献减少呢?

答案是,可以。

怎么做?

将节点 55 的权值修改为 33 就行了,若如此做,其 33 个子节点的权值都可修改为 11,故权值之和为 1×3=31 \times 3=3

则正确的染色方法为:

在这里插入图片描述

再来一组数据。 (还有完没完

数据2

根据两种情况:

  1. 一个节点的权值只受到他的父节点和子节点的影响。
  2. 这是一棵树,一定存在 dfs 序。

可以想到这道题正解其实是 树形dp

定义 fi,jf_{i,j} 为 节点 ii 的权值为 jj 时以 ii 为根节点的子树的点权总和的最小值。

现在要找一个最大可以填的数,为 log(n)\log(n)

我们可以出于最坏的情况来进行考虑,当周围的点呈现出 12481、2、4、8、…… 这样的倍增的情况的时候,我们可以发现,中间的点按照升序排列,而周围的点变成都是 11 的会更好一些,所以,最大可以填的数为 log(n)\log(n)

为了不 WAff 数组的第二维应取到 2020

所以转移方程为:

fi,j=j+xsonsiy=1log(n)min(yi:fx,y)f_{i,j} = j+ \sum_{x\in sons_i}\sum_{y=1}^{\log(n)} \min(y\neq i:f_{x,y})

AC CODE

#include <bits/stdc++.h>
using namespace std;
const int _ = 2e5 + 5;

int n, f[_][5], ans = INT_MAX;
int tot, head[_], to[_], nxt[_];

void add(int a, int b)
{
    nxt[++tot] = head[a];
    head[a] = tot;
    to[tot] = b;
}

void dfs(int x, int fa)
{
    for (int i = 1; i <= 4; i++)
        f[x][i] = i;
    for (int i = head[x]; i; i = nxt[i])
    {
        int v = to[i];
        if (v == fa)
            continue;
        dfs(v, x);
        for (int j = 1; j <= 4; j++)
        {
            int minn = INT_MAX;
            for (int k = 1; k <= 4; k++)
                if (j != k)
                    minn = min(minn, f[v][k]);
            f[x][j] += minn;
        }
    }
}

signed main()
{
    scanf("%d", &n);
    for (int i = 1, a, b; i < n; ++i)
    {
        scanf("%d%d", &a, &b);
        add(a, b);
        add(b, a);
    }
    dfs(1, 0);
    for (int i = 1; i <= 4; i++)
        ans = min(ans, f[1][i]);
    printf("%d", ans);
    return 0;
}
posted @ 2021-07-06 16:40  蒟蒻orz  阅读(3)  评论(0编辑  收藏  举报  来源