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

10.14 闲话:KTT

10.14 闲话:KTT

Part.1 基本算法

引入这样一个问题(其实这并不是板子,但是笔者认为这是此算法的另一种理解方式):

luogu P5693 EI 的第六分块

区间加,区间最大子段和。

这东西有个十分重要的性质,所有加的数都是正数。

先考虑怎么单点修,这是山海经,只需维护 \(lmax, rmax, totmax, sum\),竟然如此轻易。

那么现在扩展到区间修改:

首先肯定需要维护刚才提到的四个值,但是修改咋修改这些东西呢。

不难发现肯定就是形如区间加 \(len\times x\),其中 \(len\) 表示这个值对应的区间长度。

那么转化一下就变为了每个节点维护 \(O(len)\) 个一次函数,每次横坐标加 \(x\),询问最大值。

这的确是李超能做到的,但李超最难受的一点便是无法删除(只能撤销),那么如果只涉及这一个节点绝对是正确的,可是如果子节点被修改,那么 PushUp 就会修改区间内的线段,我们不得不暴力重构,这样就假了。

接下来接着挖掘性质,通过观察力可得:\(lmax, rmax, totmax\) 的区间长度单调不减,证明比较简单。

这启发了我们,也就是至多进行 \(O(n\log n)\) 次修改!

现在切入正题:

如果我们可以得知什么时候会发生上述区间长度的增长,那么复杂度就有了保证,可是如何维护这个问题呢,可以通过如下问题更直观的感受(这两个问题其实维护的信息基本一致,不过如下问题更为简洁):

\(n\)\(k_i x+b_i\) 的一次函数,每次区间给 \(x\) 加,区间询问最大值。

如图,不难发现,函数之间的大小关系是“单调”的,像这样的大小关系变换称为一次“击败”事件。

由此,可以维护出产生任意一次击败事件的最小的 \(\Delta x\) 当增加的 \(x\) 高过这个阈值就暴力重构。

惊奇的发现,这个击败事件其实就和上述的 \(lmax\) 等区间长度增长是很像的。

于是这样干复杂度大概就是 \(O(n\log^2 n)\) 的了,经过更严谨的势能分析可以发现是 \(O(n\log^3 n)\) 的,不过实际常数很小,可以近似看做 \(O(n\log^2 n)\)

总复杂度:\(O((n+m)\log^3 n)\)

具体实现就是对于两个一次函数求交点,然后对这个交点的横坐标取个 \(min\) 就行。

回到原问题,既然已经知道了 \(lmax, rmax, totmax, sum\) 的更新方法,那么只需要对 \(lmax, rmax, totmax\) 三个转移的交点统统取 \(min\) 就好。

细节:

  1. \(\Delta x\) 要开浮点数。
  2. 可以封一个一次函数减小码量。
  3. 查询合并两个区间答案使用结构体更加便捷。
EI 的第六分块
#include <iostream>
#include <algorithm>using namespace std;const int N = 4e5 + 10;#define int long long
using ld = long double;
const ld double_inf = 1e100;class Poly
{public :int k, b;Poly operator + (Poly x){return {k + x.k, b + x.b};}Poly operator + (int x){return {k, b + k * x};}
};Poly max(Poly x, Poly y)
{if (x.b != y.b) return x.b < y.b ? y : x;return x.k < y.k ? y : x;
}ld calc(Poly x, Poly y)
{if (x.k == y.k) return double_inf;ld pos = (ld)(x.b - y.b) / (ld)(y.k - x.k);if (pos <= 0) return double_inf;return pos;
}struct Node
{int lmax, rmax, totmax, sum;friend Node operator + (Node x, Node y){Node tmp;tmp.sum = x.sum + y.sum;tmp.lmax = max(x.lmax, x.sum + y.lmax);tmp.rmax = max(y.rmax, y.sum + x.rmax);tmp.totmax = max({x.totmax, y.totmax, x.rmax + y.lmax});return tmp;}
};int a[N];class SemTree
{#define lid id << 1#define rid id << 1 | 1public :Poly lmax[N << 2], rmax[N << 2], totmax[N << 2], sum[N << 2];ld x[N << 2];int tag[N << 2], L[N << 2], R[N << 2];void PushUp(int id){sum[id] = sum[lid] + sum[rid];lmax[id] = max(lmax[lid], sum[lid] + lmax[rid]);rmax[id] = max(rmax[rid], sum[rid] + rmax[lid]);totmax[id] = max(max(totmax[lid], totmax[rid]), rmax[lid] + lmax[rid]);x[id] = min(x[lid], x[rid]);x[id] = min(x[id], calc(lmax[lid], sum[lid] + lmax[rid]));x[id] = min(x[id], calc(rmax[rid], sum[rid] + rmax[lid]));x[id] = min(x[id], min({calc(totmax[lid], totmax[rid]), calc(max(totmax[lid], totmax[rid]), rmax[lid] + lmax[rid])}));// x[id] = min(x[id], min({calc(totmax[lid], totmax[rid]), calc(totmax[lid], rmax[lid] + lmax[rid]), calc(totmax[rid], rmax[lid] + lmax[rid])}));}void Push(int id, int v){tag[id] += v;lmax[id] = lmax[id] + v;rmax[id] = rmax[id] + v;totmax[id] = totmax[id] + v;sum[id] = sum[id] + v;x[id] -= v;}void PushDown(int id){if (tag[id]){Push(lid, tag[id]), Push(rid, tag[id]);tag[id] = 0;}}void ReBuild(int id){if (x[id] <= 0){PushDown(id);x[id] = double_inf;ReBuild(lid), ReBuild(rid);PushUp(id);}}void Build(int id, int l, int r){x[id] = double_inf;L[id] = l;R[id] = r;if (l == r){Poly tmp = {1, a[l]};x[id] = double_inf, lmax[id] = rmax[id] = totmax[id] = sum[id] = tmp;return ;}int mid = (l + r) >> 1;Build(lid, l, mid), Build(rid, mid + 1, r);PushUp(id);}void Update(int id, int cl, int cr, int l, int r, int val){if (l <= cl && cr <= r) return Push(id, val);int mid = (cl + cr) >> 1;PushDown(id);ReBuild(id);if (l <= mid) Update(lid, cl, mid, l, r, val);if (r > mid) Update(rid, mid + 1, cr, l, r, val);PushUp(id);}Node Query(int id, int cl, int cr, int l, int r){if (l <= cl && cr <= r) return {lmax[id].b, rmax[id].b, totmax[id].b, sum[id].b};int mid = (cl + cr) >> 1;PushDown(id);ReBuild(id);if (r <= mid) return Query(lid, cl, mid, l, r);else if (l > mid) return Query(rid, mid + 1, cr, l, r);else return Query(lid, cl, mid, l, r) + Query(rid, mid + 1, cr, l, r);}
}T;signed main()
{// freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);int n, q; cin >> n >> q;for (int i = 1; i <= n; i++) cin >> a[i];T.Build(1, 1, n);for (int i = 1; i <= q; i++){int op, l, r; cin >> op >> l >> r;if (op == 1){int x; cin >> x;T.Update(1, 1, n, l, r, x);}else{cout << max(0ll, T.Query(1, 1, n, l, r).totmax) << '\n';}}return 0;
}

Part.2 例题

这东西出现概率还是蛮低的。

一道比较好的题:CF 1830F

http://www.rkmt.cn/news/21090.html

相关文章:

  • 汽车价格战全面熄火了?不卷价格该卷什么? - 教程
  • 详细介绍:并发编程原理与实战(三十三)AQS框架下手写简易可重入锁的实战解析
  • U-Boot启动探秘:从汇编到命令行的奇幻之旅 - 指南
  • 两数相加-leetcode
  • 线程共享区域
  • 运行时数据区
  • AI4S Cup学习赛 - 超导体临界温度预测
  • Linux之线程池 - 指南
  • 5G x 工业应用:探索德承工控机在5G工业应用中所扮演的关键角色 - 实践
  • 背叛 仇恨 消极 如刀子刺穿了铁心 嘲笑 嗤之以鼻 漠然后只剩下孤寂
  • 【论文复现上新】AAAI2025!北理工团队提出FBRT-YOLO:面向实时航拍图像更快更好的目标检测 |计算机视觉|目标检测
  • 亚马逊因暗黑模式订阅设计支付25亿美元和解金
  • 2025年排烟风机厂家推荐榜:混流风机|管道风机|排烟风机|离心风机|轴流风机|轴流风机厂家,专注高效消防与节能,助力多行业绿色升级
  • 详细介绍:iCloud照片共享:在家庭内外分享iCloud照片
  • 对static新的认识
  • Excel - lookup()
  • 2025 佛山铝合金/系统/断桥铝/耐用/推拉/封阳台/别墅/静音门窗厂家品牌实力推荐:聚焦技术与服务的五大优选标杆
  • 说说新版畅联云的一些重要约定
  • App.vue(完整可运行示例)
  • Avalonia Behaviors 在 StackPanel 空白处无效问题解析与解决方案
  • 完整教程:Django 入门:快速构建 Python Web 应用的强大框架
  • 高级语言程序第一次作业
  • Windows MySQL 管理
  • 数据流通合规新基建 隐私计算平台的三重安全防线
  • 小程序分享
  • 图论 Walks Trails and Paths in Graph Theory 路径,链,简单路径
  • 2025 年国内面板生产厂家最新推荐排行榜,涵盖耐用 / 肤感 / 半透 / 防指纹 / 电镀 / 防静电面板等多特性优质面板厂家推荐
  • 淘宝店铺全量商品接口深度开发:从分页优化到数据完整性保障 - 实践
  • 敏捷研发管理工具深度测评:ONES、Jira、YouTrack 等 10 款全维度分析
  • 护理白板系统统一外网映射配置