2022ICPC昆明F
2022/4/17 23:17:48
本文主要是介绍2022ICPC昆明F,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
2022ICPC昆明F
题目链接
不难看出最终答案为\((sum/num)^2/4\)。问题转化为在树上找到一条简单路径,使得路径点权和除以点数绝对值最大。
考虑二分,二分出平均值并在树上\(dp\)处理。在此清华大学赛时代码使用了预处理树后续遍历的方式解决了此题,不仅避免了树形\(dp\)搜索途中的\(for\)循环嵌套与重复递归调用,更是大大增加了代码可读性:
vector<int> vs; void dfs0(int x, int f = 0) { fa[x] = f; for(auto v :g[x]) if(v != f) { dfs0(v, x); } vs.push_back(x);//创建序列 } bool calc(double V) { for(auto x : vs) { for(auto v : g[x]) if(v != fa[x]) { //do something } } if(pd) return true; else return false; }
回到本题,题目中特别强调了简单路径长度大于\(1\),那么在维护\(dp\)时需要特别注意:我们不能用可能为单独点集的情况更新\(ans\)。在此可以用树上背包的常用方式——\(x\)为根的子树会逐渐增加大小,在更新答案后再更新背包大小。在此题中可以先更新\(ans\)再更新\(dp\)值做到。在此\(dp[x]\)为以\(x\)为根的最长路径,\(ans[x]\)为以\(x\)为根的子树答案。
#include <iostream> #include <string.h> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <set> #include <vector> #include <queue> #include <map> #include <unordered_map> #define Lint long long #define pi acos(-1.0) using namespace std; const int N = 1e5 + 10; int n, f[N]; double a[N], dp[N], ans[N]; vector<int> v[N], s; void dfs(int x, int fa) { f[x] = fa; for (auto i : v[x]) { if (i == f[x]) continue; dfs(i, x); } s.push_back(x); } bool check(double x) { for (auto i : s) { ans[i] = -1e9; dp[i] = a[i] - x; if (v[i].size() == 1 && f[i]) { ans[i] = -1e9; dp[i] = a[i] - x; } else { for (auto j : v[i]) { if (j == f[i]) continue; ans[i] = max(dp[i] + dp[j], ans[i]); dp[i] = max(dp[i], dp[j] + a[i] - x); ans[i] = max(ans[i], ans[j]); } } } if (ans[1] >= 0) return true; return false; } void solve() { int x, y; scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%lf", &a[i]); for (int i = 1; i < n; ++i) { scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } dfs(1, 0); double l = 0, r = 100000; double ans = 0.0; for (int i = 1; i <= 50; ++i) { double mid = (l + r) / 2.0; if (check(mid)) { ans = mid; l = mid; } else { r = mid; } } for (int i = 1; i <= n; ++i) a[i] = -1 * a[i]; l = 0, r = 100000; for (int i = 1; i <= 50; ++i) { double mid = (l + r) / 2.0; if (check(mid)) { ans = max(ans, mid); l = mid; } else { r = mid; } } printf("%.10lf\n", ans * ans / 4.0); } int main() { // cin.tie(0); // cout.tie(0); // ios::sync_with_stdio(0); solve(); return 0; }
此写法复杂度为\(O(nlogn)\),虽然复杂度完全正确但由于牛客评测机严重波动问题使其运行速度在\(400ms\)到\(TLE\)不等。标程证明了点的选择数目为\(2\)或\(3\)个,在此给出队友的简要证明:任意一个大于\(3\)的点集都可被分组为两个大于等于\(2\)的点集,在此之中必然存在一个点集使得平均值大于等于原点集,选择此小点集答案必然更优。复杂度为\(O(n)\)。
这篇关于2022ICPC昆明F的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享