博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【题解】Atcoder ARC#96 F-Sweet Alchemy
阅读量:4971 次
发布时间:2019-06-12

本文共 3166 字,大约阅读时间需要 10 分钟。

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

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

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

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

  考虑两个物品 \(v_{i}, w_{i}\) 和 \(v_{j}, w_{j}\),其中 \(\frac{v_{i}}{w_{i}} > \frac{v_{j}}{w_{j}}\) 即 \(i\) 物品的性价比高于 \(j\)。如果我们选择了 \(v_{i}\) 个物品 \(j\) ,不如直接更换成 \(v_{j}\) 个物品 \(i\)。这样权值是相等的,都是 \(v_{i}*v_{j}\),但代价却更小:\(v_{j} * w_{i} < v_{i} * w_{j} \)。由此我们知道:在可以选择性价比更高的物品却没有选择的情况下,性价比低的物品最多选择 \(v_{i} - 1\) 个。而这个 \(v\) 的范围是很小的,所以我们可以对于每一种物品都从其中拿出 \(min(n, D)\) 件来进行多重背包,剩下的贪心即可。

#include 
using namespace std;#define maxn 55#define maxm 125005#define int long long#define INF 1e9 + 10int 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;}

 

转载于:https://www.cnblogs.com/twilight-sx/p/9376934.html

你可能感兴趣的文章
hive安装以及hive on spark
查看>>
勇者无畏
查看>>
12864点阵液晶显示模块的原理和实例程序(HJ12864M-1)
查看>>
jz1074 【基础】寻找2的幂
查看>>
Wannafly模拟赛5 A 思维 D 暴力
查看>>
C#控制台程序实现鼠标左右手习惯切换
查看>>
C++ 继承、函数重载
查看>>
Javascript获取select下拉框选中的的值
查看>>
并发编程注意的问题
查看>>
angular--ngResource的简单使用
查看>>
android本地数据库,微信数据库WCDB for Android 使用实例
查看>>
如何快速三个月成为一个领域的高手的四个方法
查看>>
[51nod]1347 旋转字符串
查看>>
SpringBoot2.0 + SpringCloud Eureka搭建高可用注册中心(Eureka之三)
查看>>
tomcat文件夹与文件解析
查看>>
【Linux开发】CCS远程调试ARM,AM4378
查看>>
springmvc常用注解标签详解
查看>>
Linux之ssh服务介绍
查看>>
Sql语句里的递归查询(转)
查看>>
[JAVA]《Java 核心技术》(一)
查看>>