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 字符环:就是将给定的一个字符串 ...
随机推荐
- dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息
在 WPF 触摸应用中,插入触摸设备,即可在应用里面使用上插入的触摸设备.在 WPF 使用触摸设备的触摸时,需要获取到触摸设备的信息,才能实现触摸 获取触摸设备插入 在 WPF 中,通过 Window ...
- Python多线程编程深度探索:从入门到实战
title: Python多线程编程深度探索:从入门到实战 date: 2024/4/28 18:57:17 updated: 2024/4/28 18:57:17 categories: 后端开发 ...
- Linux查看文件指定行数内容与查找文件内容
Linux查看文件指定行数内容 1.tail date.log 输出文件末尾的内容,默认10行 tail -20 date.log 输出最后20行的内容 tail -n -20 date.log 输出 ...
- HDU-Employment Planning题解
题目在这里 -------------------------------- Employment Planning 简单的一道dp 关键的点在于想到用枚举实现各种情况的讨论 关键的注释写在代码里了 ...
- 远程控制软件 TeamViewer 的局限性和替代方案
TeamViewer 公司创建于2005年,总部位于德国,客户遍及全球,其中企业用户居多,其各方面性能都很不错,但价格却非常贵.针对个人用户,TeamViewer 提供免费版软件,但时不时会提示&qu ...
- pageoffice在线编辑word文件并禁止选中
一.整篇文档禁止选中 wordDoc.setDisableWindowSelection(true); //禁止word的选择文字功能 二.根据条件判断是否禁止选中 比如:选中内容超过一定字数,取消选 ...
- java启动参考
启动参数 mvn clean package -Dmaven.test.skip=true -Ptest - java - -server - -Xms2G - -Xmx2G - -Xss256K - ...
- winform 使用Clipboard 和windows Word Com组件 把Html 导出到word
首先是把Html复制到剪贴板 见:https://www.cnblogs.com/HelloQLQ/p/16289343.html 然后使用: private void saveAsWordCopy( ...
- mysql 5.6 inner join and 慢优化
慢示例: SELECT o.id FROM o_schoolnotice o INNER JOIN o_schoolnotice_parent p ON o.id = p.noticeid AND p ...
- 基于React的SSG静态站点渲染方案
基于React的SSG静态站点渲染方案 静态站点生成SSG - Static Site Generation是一种在构建时生成静态HTML等文件资源的方法,其可以完全不需要服务端的运行,通过预先生成静 ...