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 字符环:就是将给定的一个字符串 ...
随机推荐
- vue+element设置选择日期最大范围(普通版)
效果是只能跟当天时间有关(30天),下一篇将来的任意时段,比较符合实际 <!DOCTYPE html> <html> <head> <meta charset ...
- Solution Set - NOI级别真题选做
[NOI2007] 社交网络 Link&Submission. key:Floyd Floyd求出任意两点间最短路,以及最短路的条数.求点 \(k\) 的答案时枚举所有点对 \(i,j\),若 ...
- golang 中 strings 包的 Replace 用法介绍笔记
函数申明: func Replace(s, old, new string, n int) string 官方描述为: 返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换 ...
- 技术书籍 — EffectiveMordenCpp 研读
一.类型推导 PROs: 源码某处的类型修改,可以自动传播其他地方 Cons: 会让代码更复杂(How?) 在模板类型推导时,有引用的实参会被视为无引用,他们的引用会被忽略 template<t ...
- nim 8. 异常处理
基本的异常处理 try: let d = newSeq[int]() echo "value: ", d[2] except: let e = getCurrentExceptio ...
- Linux系统中如何部署php
1. 在线安装 Apache 服务器 ubuntu 可通过"apt"等命令在线安装,centos用yum. # ubuntu sudo apt-get install apache ...
- Clion代码自动格式化保存
目录 前言 使用外部工具Artistic Style Clion 插件配置 注意 前言 使用Clion的时候,可以自动格式化代码的操作. 使用外部工具Artistic Style 序号 名称 地址 1 ...
- C 语言编程 — 头文件
目录 文章目录 目录 前文列表 头文件 只引用一次头文件 有条件引用 global.h 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> <C ...
- Jenkins 项目构建平台
引言 Jenkins是一个开源的.提供友好操作界面的持续集成(CI)工具,主要用于持续.自动的构建/测试软件项目.监控外部任务的运行.Jenkins 用 Java 语言编写,可在 Tomcat 等流行 ...
- rabbitmq添加延时通道时报错
rabbitmq添加延时通道时报错 'x-delayed-type' must be an existing exchange type 解决方案: 我实际用的是x-delayed-type:topi ...