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 摇钱树的更多相关文章

  1. 摇钱树运营小工具UI设计.vsd

    去年,我负责公司的一个互联网投融资平台——摇钱树.系统运营过程中,业务和客服那边不断的反馈一些事情让技术这边协助实现.例如,土豪客户忘记登录密码后懒得自己重置,更愿意选择搭讪客服MM:再比如,客户多次 ...

  2. BZOJ 5039: [Jsoi2014]序列维护

    5039: [Jsoi2014]序列维护 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 282  Solved: 169[Submit][Status ...

  3. hdu 5039 线段树+dfs序

    http://acm.hdu.edu.cn/showproblem.php?pid=5039 给定一棵树,边权为0/1.m个操作支持翻转一条边的权值或者询问树上有多少条路径的边权和为奇数. 用树形df ...

  4. 洛谷 P1987 摇钱树

    题目戳 题目描述 Cpg 正在游览一个梦中之城,在这个城市中有n棵摇钱树...这下,可让Cpg看傻了...可是Cpg只能在这个城市中呆K天,但是现在摇钱树已经成熟了,每天每棵都会掉下不同的金币(不属于 ...

  5. P1987 摇钱树

    题意:有n棵摇钱树,k天,每天可砍一棵并获得其金币    每棵树初始有$a_i$个金币,每天减少$b_i$个 问k天得到的最多金币数 这题很明显是DP(锻炼自己的机会来了QAQ) 设$f[i][j]$ ...

  6. Leetcode 5039. 移动石子直到连续

    第134次周赛 5039. 移动石子直到连续 5039. 移动石子直到连续 三枚石子放置在数轴上,位置分别为 a,b,c. 每一回合,我们假设这三枚石子当前分别位于位置 x, y, z 且 x < ...

  7. HDU 5039 Hilarity

    题意:一棵树n个结点,每条边有0.1两种权值,每次询问权值为奇数的路径数目,或者改变某一条边的权值. 分析:这个题目很巧妙低利用了异或和的特性,dfs得到每个点到根结点的权值异或和,然后奇数则为1,偶 ...

  8. 洛谷 - P1987 - 摇钱树 - dp - 贪心

    https://www.luogu.org/problemnew/show/P1987 这道题,假如是n==k,也就是把所有的树都砍完,我就知道要贪心去做,因为树给的初始金币是固定的,每天掉金币,当然 ...

  9. ECNUOJ 2142 放书

    放书 Time Limit:1000MS Memory Limit:65536KBTotal Submit:409 Accepted:173 Description  你要把一叠书放进一些箱子里面,为 ...

  10. ECNUOJ 2147 字符环

    字符环 Time Limit:1000MS Memory Limit:65536KBTotal Submit:562 Accepted:146 Description  字符环:就是将给定的一个字符串 ...

随机推荐

  1. jqGrid--统计列

    //数据表格 <div class="gridPanel" style="width:100%;"> @* 数据表格 *@ <table id ...

  2. VSCode 打开ESP32工程问题

    一.无法跳转 问题现象: 打开ESP32工程头文件提示波浪线不跳转,如下图所示: 解决办法: 删除工程中.vsccode文件夹下的所有文件 VSCode 中打开命令行搜索 ESP-IDF 找到`添加 ...

  3. 【爬虫数据集】李子柒YouTube频道TOP10热门视频的TOP2000热门评论,共计2W条

    目录 一.背景 二.爬取目标 三.结果展示 四.演示视频 五.附完整数据 一.背景 这段时间,有超多小伙伴找我要YouTube数据,做数据分析.情感分析之类的研究工作,但很多人并不是计算机软件相关专业 ...

  4. 「IT运维迷宫」那些让人头疼的常见问题与破局之道

    在数字化浪潮汹涌的今天,IT运维如同一座错综复杂的迷宫,稍有不慎便可能迷失方向.作为企业运营的幕后英雄,运维团队常常面临着各种突如其来的挑战.本文将带你深入探索IT运维中的那些常见"坑&qu ...

  5. MacOS安装gprMax教程

    原文发布于:https://blog.zhaoxuan.site/archives/19.html: 第一时间获取最新文章请关注博客个人站:https://blog.zhaoxuan.site. 1. ...

  6. 密码学—RSA公钥算法Python程序

    RSA流程 选取两个素数p,q,保密p,q 计算出n = p×q ,公开n 计算φ(n)=(p-1)(q-1) ,保密φ(n) 选择一个数e ,e满足:e < φ(n) , gcd(e,φ(n) ...

  7. lvs之DR模式的实操演练

    理论 我是内部服务,代替我访问外部网络,这是正向代理:代替外部网络访问我,这是反向代理 槽位 sh根据源地址.调度到某个节点,dh,根据目标地址,调度到某个节点, 实战演练 默认策略以及修改策略 查看 ...

  8. Github打不开解决办法(最新有效)

    Github打不开解决办法(最新有效) 1.  先看没解决之前的截图: 2.  解决方法(手动修改DNS): 2.1  以win11为例,第一步:打开 设置 - 网络和Internet,找到 高级网络 ...

  9. 为什么我们要用Spring Boot

    最近我面试了不少人,其中不乏说对 Spring Boot 非常熟悉的,然后当我问到一些 Spring Boot 核心功能和原理的时候,没人能说得上来,或者说不到点上,可以说一个问题就问趴下了! 这是我 ...

  10. Redis 的简单介绍

    Redis 特点 单线程 执行过程按顺序执行,不会同时执行多个操作,保证操作的原子性,省去了很多上下文切换线程的时间,不必考虑资源竞争和可能出现死锁. 为什么使用单线程 ? 官方FAQ表示:因为 Re ...