当前位置: 首页 > article >正文

【CF】Day72——Codeforces Round 890 (Div. 2) CDE1 (二分答案 | 交互 + 分治 | ⭐树上背包)

C. To Become Max

题目:

思路:

二分挺好想的,但是check有点不好写

看到最大值,试试二分,如果 x 可以,那么 x - 1 肯定也可以,所以具有单调性,考虑二分

如何check呢?由于 n 很小,我们可以考虑 n² 的 check,我们可以考虑枚举每一个位置为 mid,那么如果这个位置要是 mid 那么下一个位置就起码是 mid - 1,以此类推,直接模拟即可

特别的,由于 n 处无法继续往后,所以记得判断一下

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"void solve()
{int n, K;cin >> n >> K;vector<int> A(n);int mx = 0;for (int i = 0; i < n; i++){cin >> A[i];mx = max(A[i], mx);}auto check = [&](int mid) ->bool{for (int i = 0; i < n - 1; i++){int nd = 0;int m = mid;int j = i;for (; j < n; j++){if (A[j] >= m){break;}nd += m - A[j];m--;}if (K >= nd && A[j] >= m && j < n){return true;}}return false;};int l = mx, r = 1e18;while (l + 1 < r){int mid = l + r >> 1;if (check(mid)){l = mid;}else{r = mid;}}if (check(r)){cout << r << endl;return;}cout << l << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}

D. More Wrong

题目:

思路:

找结论题

交互题通常喜欢二分,这里也不例外

我们先要知道一个关键点,假设 x 是最大值的位置,那么对于任意一个左端点 l,都有以下结论: [l, x-1] = [l,  x],即这两个区间的逆序对一定相同,因为 x 是最大值,那么加在后面是无影响的 

如果我们直接一个一个枚举的话肯定会超时,所以我们考虑优化,我们考虑分治,我们像线段树一样每次取一半,然后把每个子区间的最大值求出来,再依次合并

具体的,假设我们要求 [l r] 的最大值,我们要先求出 [l mid] [mid+1 r] 的两个最大值的位置 Lmx和 Rmx,其中 mid = (l+r) / 2,然后根据我们的结论,如果 Rmx 是最大值,那么就有 [Lmx Rmx - 1] = [Lmx Rmx],否则就是 Lmx 是区间的最大值

模拟即可

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"int ask(int l,int r)
{if (l == r)return 0;else{cout << "? " << l << " " << r << endl;int w = 0;cin >> w;return w;}
}int solve(int l,int r)
{if (l == r){return r;}else{int mid = l + r >> 1;int Lmx = solve(l, mid), Rmx = solve(mid + 1, r);if (ask(Lmx,Rmx) == ask(Lmx,Rmx-1)){return Rmx;}return Lmx;}
}signed main()
{int t = 1;cin >> t;while (t--){int n; cin >> n;int w = solve(1, n);cout << "! " << w << endl;}return 0;
}

E1. PermuTree (easy version)

题目:

思路:

看似lca?实则dp

这一题我们看着好像无法下手,但是注意到 n <= 5000,我们可以考虑 n² 做法

题目意思转化一下就是你可以自由分配树的顶点的权值 val[i],使得所有权值最后是一个排列,最后的答案是对于任意一个父节点选取其两个子节点 (u,v),满足 val[u] < val[fa] < val[v] 的(u,v)对数

我们可以将 lca,变化一下,因为我们其实不需要知道子节点具体是什么,我们只在乎它的权值,那么我们就可以考虑枚举每一个节点当这个 lca

那么如果这个点是 lca,那我们如何计算其奉献呢?我们贪心的想,我们肯定是考虑将其子树分成两个子集,一个是权值全小于父节点,一个是权值全大于父节点,那么答案就是 size_small * size_big

那么问题再转化一下,对于每一个子树,我们可以考虑其是大于还是小于父节点,然后对于所有情况求最大值,那么这其实就是一个树上01背包,对于每个子树我们都可以选or不选,所以直接套就行了

那为什么我们一定可以这样构造呢?我们这样想,我们肯定是先分配最小的子树,比如对于下面例子

我们可以分配给子树小的,然后再给父节点一个中间值,然后再分配比父节点大的子树,如 1 2 | 3 | 4 5 | 6 7 这样,不过由于不需要我们输出具体的权值,所以我们不需要考虑具体的构造,但是肯定是能构造的 

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"int ans = 0;
int treesize[5005];
vector<vector<int>> g(5005);
void dfs(int fa)
{treesize[fa] = 1;vector<int> sontreesize;for (auto & son : g[fa]){dfs(son);treesize[fa] += treesize[son];sontreesize.push_back(treesize[son]);}vector<int> f(5005, 0);f[0] = 1;int sum = 0;for (auto& sonsize : sontreesize){sum += sonsize;for (int i = sum; i >= sonsize; i--){f[i] |= f[i - sonsize];}}int mx = 0;for (int i = 0; i <= sum; i++){if (f[i]){mx = max(mx, i * (sum - i));}}ans += mx;
}void solve()
{int n;cin >> n;for (int i = 2; i <= n; i++){int fa; cin >> fa;g[fa].push_back(i);}dfs(1);cout << ans << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;while (t--){solve();}return 0;
}

http://www.lryc.cn/news/2396211.html

相关文章:

  • 单片机寄存器的四种主要类型!
  • 智能嗅探AJAX触发:机器学习在动态渲染中的创新应用
  • 【计算机网络】Linux下简单的UDP服务器(超详细)
  • Java并发编程实战 Day 3:volatile关键字与内存可见性
  • 华为OD机试真题——报文回路(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
  • K8s工作流程与YAML实用指南
  • 功能丰富的PDF处理免费软件推荐
  • Java补充(Java8新特性)(和IO都很重要)
  • pycharm debug的时候无法debug到指定的位置就停住不动了
  • 分布式流处理与消息传递——Kafka ISR(In-Sync Replicas)算法深度解析
  • 极大似然估计例题——正态分布的极大似然估计
  • Pull Request Integration 拉取请求集成
  • OS10.【Linux】yum命令
  • 头歌数据库课程实验(角色管理)
  • 【android bluetooth 协议分析 03】【蓝牙扫描详解 1】【扫描关键函数 btif_dm_search_devices_evt 分析】
  • SpringBoot使用ThreadLocal保存登录用户信息
  • 多模态大语言模型arxiv论文略读(102)
  • Ubuntu系统如何部署Crawlab爬虫管理平台(通过docker部署)
  • python常用库-pandas、Hugging Face的datasets库(大模型之JSONL(JSON Lines))
  • 高端装备制造企业如何选择适配的项目管理系统提升项目执行效率?附选型案例
  • 【Dv3Admin】工具权限配置文件解析
  • AI炼丹日志-22 - MCP 自动操作 Figma+Cursor 自动设计原型
  • Python爬虫:AutoScraper 库详细使用大全(一个智能、自动、轻量级的网络爬虫)
  • 2025.6.1总结
  • [嵌入式实验]实验四:串口打印电压及温度
  • LVS+Keepalived 高可用
  • Linux正则三剑客篇
  • HTML5 视频播放器:从基础到进阶的实现指南
  • 鸿蒙HarmonyOS (React Native)的实战教程
  • 函数栈帧深度解析:从寄存器操作看函数调用机制