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

做题记录3

CF2149E. Hidden Knowledge of the Ancients

思路

滑动窗口 + 双指针。

先不考虑长度的限制,求"恰好有 \(k\) 个不同的数"的区间。可以维护两个窗口,一个是以当前的位置为右端点,且第一个最多有 \(k\) 个不同元素 的区间;一个是以当前位置为右端点,且第一个 最多有 \(k - 1\) 个不同元素 的区间。那么以当前位置为右端点,且恰好有 \(k\) 个不同元素的子区间的数量就为 最多 \(k - 1\) 个 - 最多 \(k\)。其实就是它们的左端点相减的结果。再加上长度限制,要满足

\[l \leq (右端点 - 左端点 + 1) \leq r \Rightarrow 右 - r + 1 \leq 左 \leq 右 - l + 1 \]

所以对于当前的右端点,合法的左端点的数量就为 \(\min(最多 k - 1 个的左端点, c - l + 1) - \max(最多 k 个的右端点, c - r + 1)\)

由于 \(a\) 的范围很大,所以要先离散化。

代码

void solve(void) {int n, k, l, r;std::cin >> n >> k >> l >> r;std::vector<int> a(n);for(int i = 0; i < n; i++) std::cin >> a[i];auto tmp = a;std::sort(tmp.begin(), tmp.end());tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());for(auto &i : a) {i = std::lower_bound(tmp.begin(), tmp.end(), i) - tmp.begin();}int len  = tmp.size();i64 ans = 0;std::vector<int> K(len), K1(len);int disk = 0, disk1 = 0;int lk = 0, lk1 = 0;for(int i = 0; i < n; i++) {K[a[i]]++;if(K[a[i]] == 1) {disk++;}while(disk > k) {int x = a[lk++];K[x]--;if(K[x] == 0) {disk--;}}K1[a[i]]++;if(K1[a[i]] == 1) {disk1++;}while(disk1 > k - 1) {int x = a[lk1++];K1[x]--;if(K1[x] == 0) {disk1--;}}int L = std::max(lk, i - r + 1);int R = std::min(lk1 - 1, i - l + 1);if(R >= L) ans += R - L + 1;}std::cout << ans << '\n';
}

CF2149F. Nezuko in the Clearing

思路

二分。

首先注意到,无论如何都是要走 \(d\) 个回合的,但是在这期间可能会休息若干次。因此可以表示为将 \(d\) 个回合分割为了 \(n\) 段,每段连续走 \(k_i\) 个回合,然后停下来休息一次,最后的答案就应该是:

\[d + (n - 1) \]

在这期间,血量的总消耗是:

\[\sum_{i = 1}^{n}\frac{k_i(k_i + 1)}{2} - (n - 1) \]

要保证消耗 \(\leq h - 1\) ,这样才能保证每一次触发的时候都有 \(\geq 1\) 的血量。

显然我们需要枚举分段的数量,如何 check 呢?

观察函数 \(f(k) = \frac{k(k + 1)}{2}\) ,我们想要让 \(\sum_{i = 1}^{n} f(k_i)\) 最小,注意到应该让每一段的长度都尽可能的接近,也就是说我们会分成若干段 \(\lfloor \frac{d}{n} \rfloor\) ,还有一段 \(d \mod n\) ,然后就可以判断了。

但是如何证明这个结论呢?我不会

代码

void solve(void) {i64 h, d;std::cin >> h >> d;if(h == 1) {std::cout << d * 2 << '\n';return;}i64 l = 1, r = d, len = -1;auto check = [&](i64 x) -> bool {i64 q = d / x;i64 r = d % x;i64 sum = r * (q + 1) * (q + 2) / 2;i64 sum1 = (x - r) * q * (q + 1) / 2;i64 total = sum + sum1 - (x - 1);return total <= h - 1;};while(l <= r) {i64 mid = (l + r) / 2;if(check(mid)) {r = mid - 1;len = mid;} else l = mid + 1;}//std::cout << len << '\n';std::cout << (len - 1) + d << '\n';
}
http://www.hskmm.com/?act=detail&tid=17490

相关文章:

  • oucaiclub_cheapter1
  • navicat
  • 20250925 之所思 - 人生如梦
  • 在CodeBolcks下wxSmith的C++编程教程——在屏幕上绘图和保存绘图
  • 苍穹外卖-day07(缓存菜品,缓存套餐,添加购物车,查看购物车,清空购物车) - a
  • 一次CPU飙升问题排查定位
  • ros2 control 2
  • 基于洞察的智能编程法——从直觉到代码的原型炼成术
  • lc1036-逃离大迷宫
  • 9.25学习笔记
  • 新学期每日总结(第4天)
  • VSCode 升级 C++支持版本
  • 第四天
  • 25.9.25
  • 在electron-vite使用ShadCN
  • 每日博客(补)
  • 如何使用极限网关实现 Elasticsearch 集群迁移至 Easysearch
  • 文档抽取技术:实现金融保险业务流程自动化
  • 算法作业
  • C#学习3
  • 9-23
  • 9-26
  • Ubuntu Uninstall App
  • 20250925
  • 题解:P2662 牛场围栏
  • day11 课程(学员管理系统案例)
  • c语言初步学习
  • jmeter函数
  • 【网络编程】UDP 编程实战:从套接字到聊天室多场景计划构建
  • AC自动机在线版本(alert命中报警)