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

【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;
}

相关文章:

  • 多模态大语言模型arxiv论文略读(103)
  • Java抽象工厂模式详解
  • 【计算机网络】第3章:传输层—面向连接的传输:TCP
  • Linux下目录递归拷贝的单进程实现
  • RuoYi前后端分离框架实现前后端数据传输加密(二)之前端篇
  • 如何用docker部署ELK?
  • 设计模式——观察者设计模式(行为型)
  • 在 SpringBoot+Tomcat 环境中 线程安全问题的根本原因以及哪些变量会存在线程安全的问题。
  • 设计模式——桥接设计模式(结构型)
  • NodeJS全栈开发面试题讲解——P1Node.js 基础与核心机制
  • java servlet: context-path的作用
  • 基于GeoTools和OSM路网求解两条道路相交点-以长沙市为例
  • 视觉分析明火检测助力山东化工厂火情防控
  • 【寻找Linux的奥秘】第九章:自定义SHELL
  • 【Vue 3全栈实战】从组合式API到企业级架构设计
  • 计算机基础——宏病毒防御与网络技术
  • idea中springboot2.7(由于步入另一个线程,已跳过 xxx 处的断点)
  • Go语言中的数据类型转换
  • MATLAB实战:Arduino硬件交互项目方案
  • Codeforces Round 1028 (Div. 2)A题
  • 怎样建设一个英语网站/推广普通话手抄报内容文字
  • 网站开发需要大学吗/aso优化app推广
  • 建设网站简单的需要多少天/平台营销
  • 友链网站降权/广州网站seo公司
  • 当当网网站建设/购买友情链接网站
  • 湖南岳阳最新疫情/网站搜索引擎优化的方法