ecnuoj 5039 摇钱树
5039. 摇钱树
题目链接:5039. 摇钱树
感觉在赛中的时候,完全没有考虑分数规划这种做法。同时也没有想到怎么拆这两个交和并的式子。有点难受……
当出现分数使其尽量大或者小,并且如果修改其中直接相关的某个值会导致分子分母同时变化的时候,还是要多想想分数规划的做法。
下面引用一下题解
另外这两个交和并的式子,令 \(a = S \and T, b = T - a\),所以原来的式子变成了
\[\frac{|S \and T|}{|S \or T|} = \frac{a}{b + |S|}
\]所以,用分数规划的做法,二分一个答案 \(ans\),则有
\[\frac{a}{b + |S|} \ge ans \implies a - b \cdot ans \ge |S|\cdot ans
\]接下来用树上 dp 求一个最大的 \(a - b \cdot ans\) 即可。
令 \(f_{i,j}\) 表示此时 \(i\) 号点上选了 \(j\) 个子树加和起来最大的 \(a-b\cdot ans\) 值,\(x_{i,j}\) 表示这个式子中的 \(a\),\(y_{i,j}\) 表示这个式子中的 \(b\)。
那么接下来就是一个普通的树上背包的转移了,不过区别在于转移完整个 \(f_u\) 后,需要再用取整个以 \(u\) 为根构成的一颗子树去更新一下 \(f_{u,1}\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl
template<typename Tp> IL void read(Tp &x) {
x=0; int f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
x *= f;
}
int buf[42];
template<typename Tp> IL void write(Tp x) {
int p = 0;
if(x < 0) { putchar('-'); x=-x;}
if(x == 0) { putchar('0'); return;}
while(x) {
buf[++p] = x % 10;
x /= 10;
}
for(int i=p;i;i--) putchar('0' + buf[i]);
}
const int N = 100000 + 5;
const int M = 50 + 5;
int n, m;
int a[N], suma[N], sz[N], x[N][M], y[N][M];
db f[N][M];
vector<int> G[N];
struct fs {
int fz, fm;
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b);}
fs(int fz=0, int fm=1) {
if(fz == 0) this -> fm = 1;
else {
int g = gcd(fz, fm);
this -> fz = fz / g;
this -> fm = fm / g;
}
}
};
int dfs2(int u, int fa, const db& ef_x) {
int u_leaf = 0;
if(u != 1 && G[u].size() == 1) {
if(a[u] == 1) {
f[u][1] = 1.0;
x[u][1] = 1; y[u][1] = 0;
}
return ++u_leaf;
}
for(int i=0;i<=m;i++) f[u][i] = x[u][i] = y[u][i] = 0;
for(int v : G[u]) {
if(v == fa) continue;
int v_leaf = dfs2(v, u, ef_x);
for(int i=min(u_leaf,m);i>=0;i--) {
for(int j=1;j<=v_leaf && i+j <= m; j++) {
if(f[u][i] + f[v][j] > f[u][i+j]) {
f[u][i+j] = f[u][i] + f[v][j];
x[u][i+j] = x[u][i] + x[v][j];
y[u][i+j] = y[u][i] + y[v][j];
}
}
}
u_leaf += v_leaf;
}
if(suma[u] - (sz[u] - suma[u]) * ef_x >= f[u][1]) {
f[u][1] = suma[u] - (sz[u] - suma[u]) * ef_x;
x[u][1] = suma[u];
y[u][1] = sz[u] - suma[u];
}
return u_leaf;
}
array<int, 3> check(const db& x) {
dfs2(1, 0, x);
int ansu = 1, ansi = 1;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
if(f[i][j] > f[ansu][ansi]) {
ansu = i; ansi = j;
}
}
return {f[ansu][ansi] >= suma[1] * x, ansu, ansi};
}
void dfs1(int u, int fa) {
suma[u] += a[u];
sz[u] = 1;
for(int v : G[u]) {
if(v == fa) continue;
dfs1(v, u);
suma[u] += suma[v];
sz[u] += sz[v];
}
}
void solve() {
read(n); read(m);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<n;i++) {
int u, v; read(u); read(v);
G[u].pb(v); G[v].pb(u);
}
dfs1(1, 0);
db L = 0.0, R = 1.0;
fs ans;
while(R - L >= 1e-10) {
db M = (L + R) / 2.0;
auto arr = check(M);
if(arr[0]) {L = M; ans = fs(x[arr[1]][arr[2]], y[arr[1]][arr[2]] + suma[1]);}
else R = M;
}
write(ans.fz); putchar(32); write(ans.fm); putchar(10);
}
int main() {
#ifdef LOCAL
freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
#endif
int T = 1;
// read(T);
while(T--) solve();
return 0;
}
ecnuoj 5039 摇钱树的更多相关文章
- 摇钱树运营小工具UI设计.vsd
去年,我负责公司的一个互联网投融资平台——摇钱树.系统运营过程中,业务和客服那边不断的反馈一些事情让技术这边协助实现.例如,土豪客户忘记登录密码后懒得自己重置,更愿意选择搭讪客服MM:再比如,客户多次 ...
- BZOJ 5039: [Jsoi2014]序列维护
5039: [Jsoi2014]序列维护 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 282 Solved: 169[Submit][Status ...
- hdu 5039 线段树+dfs序
http://acm.hdu.edu.cn/showproblem.php?pid=5039 给定一棵树,边权为0/1.m个操作支持翻转一条边的权值或者询问树上有多少条路径的边权和为奇数. 用树形df ...
- 洛谷 P1987 摇钱树
题目戳 题目描述 Cpg 正在游览一个梦中之城,在这个城市中有n棵摇钱树...这下,可让Cpg看傻了...可是Cpg只能在这个城市中呆K天,但是现在摇钱树已经成熟了,每天每棵都会掉下不同的金币(不属于 ...
- P1987 摇钱树
题意:有n棵摇钱树,k天,每天可砍一棵并获得其金币 每棵树初始有$a_i$个金币,每天减少$b_i$个 问k天得到的最多金币数 这题很明显是DP(锻炼自己的机会来了QAQ) 设$f[i][j]$ ...
- Leetcode 5039. 移动石子直到连续
第134次周赛 5039. 移动石子直到连续 5039. 移动石子直到连续 三枚石子放置在数轴上,位置分别为 a,b,c. 每一回合,我们假设这三枚石子当前分别位于位置 x, y, z 且 x < ...
- HDU 5039 Hilarity
题意:一棵树n个结点,每条边有0.1两种权值,每次询问权值为奇数的路径数目,或者改变某一条边的权值. 分析:这个题目很巧妙低利用了异或和的特性,dfs得到每个点到根结点的权值异或和,然后奇数则为1,偶 ...
- 洛谷 - P1987 - 摇钱树 - dp - 贪心
https://www.luogu.org/problemnew/show/P1987 这道题,假如是n==k,也就是把所有的树都砍完,我就知道要贪心去做,因为树给的初始金币是固定的,每天掉金币,当然 ...
- ECNUOJ 2142 放书
放书 Time Limit:1000MS Memory Limit:65536KBTotal Submit:409 Accepted:173 Description 你要把一叠书放进一些箱子里面,为 ...
- ECNUOJ 2147 字符环
字符环 Time Limit:1000MS Memory Limit:65536KBTotal Submit:562 Accepted:146 Description 字符环:就是将给定的一个字符串 ...
随机推荐
- SkiaSharp 渲染输出 SVG 文件
谷歌的 Skia 的一个卖点就是提供了完美的 SVG 的支持,包括输入和输出.输入指的是给一张 SVG 图片,将这个 SVG 渲染出来.输出就是将输出画面保存为 SVG 格式的图片.自然 SkiaSh ...
- SQL 多表关联更新语句
A表WHERE条件来自B表查询结果集 UPDATE a SET a.is_sync = 0 FROM A表 a LEFT JOIN B表 b ON a.order_id = b.order_id AN ...
- 【web安全】隐藏nginx头文件信息
摘要 Nginx作为开源web中间件,被广泛应用.因此源编译或者yum安装,都会带有其原有的nginx版本.很容易被针对,因此,通过修改nginx的源码.隐藏nginx版本和头部信息,保障nginx的 ...
- ansible系列(31)--ansible实战之部署WEB集群架构(1)
目录 1. WEB集群环境说明 2. ansible部署WEB集群实现思路 3. ansible基础环境部署 1. WEB集群环境说明 WEB集群环境说明如下: 客户端:模拟外网主机,地址:192.1 ...
- 2019年最新前端面试题,js程序设计题
都说机会是留给有准备的人的. 一年之计在于春,面对众多的前端技术,需要时刻充电自己. 我现在整理一些前端js面试程序题. 1.判断一个字符串中出现最多的字符,并计算出现的次数? 2.用css伪类实现下 ...
- 🔥🔥🔥httpsok-v1.8.0 SSL证书自动续签就应该这么简单
httpsok-v1.8.0 SSL证书自动续签就应该这么简单 简介 一行命令,一分钟轻松搞定SSL证书自动续期 httpsok 是一个便捷的 HTTPS 证书自动续签工具,专为 Nginx 服务器设 ...
- uniapp微信小程序uni.request捕获500异常
通常使用ajax,axios等进行服务请求,500错误或者其他的错误都会直接进入到错误通道里头,比如ajax异常的话会进入到error的回调函数里头,axios异常会进行到catch里头,一开始以为u ...
- vue3.4中defineModel中默认值是复杂数据类型 (注意!!!)
const drillFields = defineModel<string[]>('drillFields', { get(val) { return reactive(val || [ ...
- Linux环境下:程序的链接, 装载和库[动态链接]
静态链接库在程序编译阶段就完成了链接工作,完成链接后,依赖的库就都打入了可执行文件中,所以文件大小一般会比较大. 而动态库链接库是在程序运行时才被链接的,所以磁盘上只要保留一份副本,因此节约了磁盘空间 ...
- 零代码零硬件玩转华为云IoT,基于设备联动实时监控设备
本文分享自华为云社区<一键守护,实时洞察:华为云IoT设备联动,智能感知设备状态变化,精准触发告警通知[零代码零硬件玩转华为云IoT]>,作者:周周的奇妙编程. 前言 在前面我们已经体验过 ...