ICPC2020南京站 M Monster Hunter

传送门


前几天的训练赛开了去年icpc的南京站,对这题印象挺深的。我们虽然推出了树上背包的转移方程,但是实现的时候因为优化没到位一直超时……没想到我以前写的所谓的\(O(n^2)\)的树形背包竟然是假的。


首先感觉自己dp这方面还是要加强,设了正确的状态后却迟迟没有推出转移方程,最后还是队友推出来的。

\(dp[u][0/1][j]\)表示以\(u\)为根的子树中,\(u\)这个点没有/有用咒语,且整棵子树内共用了\(j\)次咒语的情况下,杀掉所有怪的最小化费。

而转移的时候考虑的是从\(u\)的所有孩子转移,即给孩子们\(v\)分配咒语次数,于是有:

\[\begin{align} dp[u][0][j] &= \min\{\sum\limits_{\sum k_i = j}dp[v][0/1][k] + a[v] * [flg_v = 0] \} + a[u], \\ dp[u][1][j] &= \min\{\sum_{\sum k_i = j - 1} dp[v][0/1][k]\} \end{align} \]

如果把每个孩子\(v\)的dp值看成物品,那就是一个分组背包:对于每个孩子,只能从\(dp[v][0/1][k]\)中选一个,而每个孩子又必须选一个。那么就变成了比较显然的树形背包了。这题稍有一些区别的地方在于,每组物品必须选一个,因此在枚举\(k\)之前向\(u\)中放任意一个,再进行dp.

关于树形背包,一定要把所有优化都加上,才能达到\(O(n^2)\)。比如枚举\(k\)的时候要考虑上下界,如果没有下界,就会被链的数据卡掉,而如果没有上界,就会被菊花的数据卡掉。训练赛的时候就是因为没有下界优化而超时的。

#include <bits/stdc++.h>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
#define Mem(a, x) memset(a, x, sizeof(a))
typedef long long ll; 
const int maxn = 2e3 + 5;
const ll INF = 0x3f3f3f3f3f3f3f3f;
In ll read()
{
    ll ans = 0;
    char ch = getchar(), las = ' ';
    while(!isdigit(ch)) las = ch, ch = getchar();
    while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    if(las == '-') ans = -ans;
    return ans;
}
In void write(ll x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}

int n, a[maxn];
struct Edge
{
    int nxt, to;
}e[maxn];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
    e[++ecnt] = (Edge){head[x], y};
    head[x] = ecnt;
}

int siz[maxn];
ll dp[maxn][2][maxn];
In void dfs(int now)
{
    siz[now] = 1;
    for(int i = 0; i <= n; ++i) dp[now][0][i] = dp[now][1][i] = INF;
    dp[now][0][0] = a[now]; dp[now][1][1] = 0;
    forE(i, now, v)
    {
        dfs(v);
        for(int j = siz[now] + siz[v]; j >= 0; --j)
        {
        	if(j) dp[now][1][j] += dp[v][0][0]; 
            dp[now][0][j] += dp[v][0][0] + a[v];
            for(int k = max(0, j - siz[now]); k <= min(j, siz[v]); ++k)
            {
                dp[now][0][j] = min(dp[now][0][j], dp[now][0][j - k] + min(dp[v][1][k], dp[v][0][k] + a[v])),
                if(j) dp[now][1][j] = min(dp[now][1][j], dp[now][1][j - k] + min(dp[v][1][k], dp[v][0][k]));
            }
        }
		siz[now] += siz[v];
    }
}

int main()
{
    int T = read();
    while(T--)
    {
        Mem(head, -1), ecnt = -1;
        n = read();
        for(int i = 2; i <= n; ++i) addEdge(read(), i);
        for(int i = 1; i <= n; ++i) a[i] = read();
        dfs(1);
        for(int i = 0; i <= n; ++i)
        {
            write(min(dp[1][0][i], dp[1][1][i]));
            i == n ? enter : space;
        }
    }
    return 0;
}
posted @ 2021-10-22 19:59  mrclr  阅读(102)  评论(0编辑  收藏  举报