【题解】Atcoder ARC#96 F-Sweet Alchemy

  首先,我们发现每一个节点所选择的次数不好直接算,因为要求一个节点被选择的次数大于等于父亲被选择的次数,且又要小于等于父亲被选择的次数 +D+D。既然如此,考虑一棵差分的树,规定每一个节点被选择的次数为 xx,表示节点实际上被选择的次数是父亲被选择的次数 +x+x。显然,这个 xx 是小于等于 DD 的。分析这样我们发现,选择了一个节点实际上对应子树内的所有节点的选择次数均增加,所以我们重新定义选择一个节点的价值为子树内(含自身)节点的个数,而代价则是子树内所有代价的总和(含自身)。

  问题转化为:在一棵树上有不超过 5050 个节点,每个节点均有一个权值及一个代价,除11 号节点外每个节点选择的次数均不能超过 DD。求在总代价不超过 xx 的前提下,如何使权值最大化?

  想到这里我就懵逼了。乍一看背包,然而代价的范围过大,根本不可能实现。突破口只有非常小的 nn 的范围了。想了很久也不会,看题解。还是非常强的。

  在最开始学背包的时候,会有一个错误的想法:对于权值为 vivi,代价为 wiwi 的若干物品,我们计算出它们的性价比,贪心的选择其中性价比高的部分物品。这样之所以是错的,是由于物品不能分割,这样会有空闲的地方出现但又不能塞入更好的物品了。于是我们考虑:在什么样的情况下可以直接用性价比高的物品代替性价比低的物品呢?

  考虑两个物品 vi,wivi,wivj,wjvj,wj,其中 viwi>vjwjviwi>vjwjii 物品的性价比高于 jj。如果我们选择了 vivi 个物品 jj ,不如直接更换成 vjvj 个物品 ii。这样权值是相等的,都是 vivjvivj,但代价却更小:vjwi<viwjvjwi<viwj。由此我们知道:在可以选择性价比更高的物品却没有选择的情况下,性价比低的物品最多选择 vi1 个。而这个 v 的范围是很小的,所以我们可以对于每一种物品都从其中拿出 min(n,D) 件来进行多重背包,剩下的贪心即可。

复制代码
#include <bits/stdc++.h>
using namespace std;
#define maxn 55
#define maxm 125005
#define int long long
#define INF 1e9 + 10
int n, X, D, ans, cnt, dp[maxm];
int w[maxn], val[maxn], pos[maxn];
int tot, V[maxn * maxn], W[maxn * maxn];
     
int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}
     
struct edge
{
    int cnp, to[maxn], last[maxn], head[maxn];
    edge() { cnp = 1; }
    void add(int u, int v) 
    { to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; }
}E1;
     
void dfs(int u)
{
    val[u] = 1;
    for(int i = E1.head[u]; i; i = E1.last[i])
    {
        int v = E1.to[i];
        dfs(v);
        w[u] += w[v]; val[u] += val[v];
    }
    cnt += val[u] * n; pos[u] = u;
}
     
bool cmp(int x, int y) { return val[x] * w[y] > val[y] * w[x]; }
void Get_min(int &x, int y) { x = x < y ? x : y; }
     
signed main()
{
    n = read(), X = read(), D = read();
    w[1] = read();
    for(int i = 2; i <= n; i ++)
    {
        w[i] = read(); int x = read();
        E1.add(x, i);
    }
    dfs(1);
    sort(pos + 1, pos + n + 1, cmp);
    int tmp = min(n, D);
    for(int i = 1; i <= cnt; i ++) dp[i] = INF;
    for(int i = 1; i <= n; i ++)
    {
        int len = 1, lim = tmp;
        while(lim >= len)
        {
            V[++ tot] = len * val[pos[i]];
            W[tot] = len * w[pos[i]];
            lim -= len, len <<= 1;
        }
        if(lim) V[++ tot] = lim * val[pos[i]], W[tot] = lim * w[pos[i]];
    }
    for(int i = 1; i <= tot; i ++)
        for(int j = cnt; ~j; j --)
                if(j >= V[i]) Get_min(dp[j], dp[j - V[i]] + W[i]); // k件物品 
    int ans = 0;
    for(int i = 0; i <= cnt; i ++)
       {
        if(dp[i] > X) continue;
        int ret = i, left = X - dp[i];
        for(int j = 1; j <= n; j ++)
        {
            int tem = pos[j], used = min(max(D - n, 0LL), left / w[tem]);
            if(tem == 1) used = left / w[tem];
            left -= w[tem] * used;
            ret += val[tem] * used;
        }
        ans = max(ans, ret);
    }
    printf("%lld\n", ans);
    return 0;
}
复制代码

 

posted @   Twilight_Sx  阅读(481)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示