At_pakencamp_2023_day1_p sol

news/2024/12/21 20:39:02/文章来源:https://www.cnblogs.com/georgeyucjr/p/18447329

题面

给你两个序列\(A, B\)\(\forall u, v(u \not = v)\)之间边的权值为\(a_ua_v+b_ub_v\)。求最小生成树的边权和。

原题目

editorial

朴素的想法

考虑类似题目的做法,考虑每一次寻找最小的然后加入。发现这种思想和Boruvka比较相似。于是我们考虑Boruvka的方式来做。

对现有的连通块的基础上考虑:我们可以将这条新的边放在连通块编号在当前连通块前面的,也可以放在连通块编号在当前编号后面的(假设对当前的连通块标编号)。那么我们需要对这两部分分开计算。那么就可以转化问题了。

转化问题

问题变成了这样:

我们要维护一个数据结构,支持每次向集合\(S\)加入一个数对\((a, b)\), 并询问时给出\((x, y)\), 求\(\min \limits_{(a, b) \in S} (ax+by)\)

这个问题在maspy的题解里面写的是用凸包或CHT

但是我不会geo的任何东西,于是转化成ds。

\(b\not = 0\)时,相当不人类智慧的考虑到\(ax+by=y(\frac{x}{y}a+b)\),看作时某个位置的一次函数最值乘上一个常数,发现可以直接李超线段树维护。

note

注意一下0的情况。至少我还需要除了线段树要计所有加入a的max,所有加入a的min,所有加入且b为0的a的max,所有加入且b为0的a的min,然后在做。详细的见代码(有点丑)

code

using db = double;
inline constexpr static db eps = 1e-10;constexpr int N = 5e4 + 5;int n, fa[N], dsu_con_cnt = 0;
ll ans = 0, val[N], a[N], b[N];
db pos[N];
vector<int> cons[N];
pii rcs[N];int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
bool merge(int u, int v) { auto du = find(u), dv = find(v); if (du != dv) return fa[du] = dv, --dsu_con_cnt, true; else return false; }struct Line {ll k, b;Line(ll _k = inf<ll>, ll _b = 0) : k(_k), b(_b) { }Line& operator=(const Line&x) { k = x.k, b = x.b; return *this; }
};struct segnode {Line l1, l2;
} seg[N << 2];# define lson index << 1
# define rson index << 1 | 1inline db calc(Line cur, db pos) {return static_cast<db>(cur.k) * pos + static_cast<db>(cur.b);
}void build(int index, int l, int r) { if (l > r) return ;seg[index].l1 = seg[index].l2 = Line(); if (l == r) return ;int mid = (l + r) >> 1;build(lson, l, mid);build(rson, mid + 1, r);
}void change1(int index, int l, int r, Line x) {if (l > r) return ;int mid = (l + r) >> 1;if (seg[index].l1.k == inf<ll> || calc(seg[index].l1, pos[mid]) < calc(x, pos[mid])) swap(seg[index].l1, x);if (x.k == inf<ll> || l == r) return ;if (calc(seg[index].l1, pos[l]) < calc(x, pos[l])) change1(lson, l, mid, x);if (calc(seg[index].l1, pos[r]) < calc(x, pos[r])) change1(rson, mid + 1, r, x);
}void query1(int index, int l, int r, int qpos, Line &cur) {if (l > r) return ;int mid = (l + r) >> 1;if (seg[index].l1.k == inf<ll>) return;if (cur.k == inf<ll> || calc(cur, pos[qpos]) < calc(seg[index].l1, pos[qpos])) cur = seg[index].l1;if (l == r) return ;if (qpos <= mid) query1(lson, l, mid, qpos, cur);else query1(rson, mid + 1, r, qpos, cur);
}void change2(int index, int l, int r, Line x) {if (l > r)return ;int mid = (l + r) >> 1;if (seg[index].l2.k == inf<ll> || calc(seg[index].l2, pos[mid]) > calc(x, pos[mid])) swap(seg[index].l2, x);if (x.k == inf<ll> || l == r) return ;if (calc(seg[index].l2, pos[l]) > calc(x, pos[l])) change2(lson, l, mid, x);if (calc(seg[index].l2, pos[r]) > calc(x, pos[r])) change2(rson, mid + 1, r, x);
}void query2(int index, int l, int r, int qpos, Line &cur) {if (l > r) return ;int mid = (l + r) >> 1;if (seg[index].l2.k == inf<ll>) return;if (cur.k == inf<ll> || calc(cur, pos[qpos]) > calc(seg[index].l2, pos[qpos])) cur = seg[index].l2;if (l == r) return ;if (qpos <= mid) query2(lson, l, mid, qpos, cur);else query2(rson, mid + 1, r, qpos, cur);
}struct segtree_cht {int sz, tot;bool seg1_added;map<pair<ll, ll>, int> idx;pair<ll, int> amx, amn, amx_zero, amn_zero;segtree_cht() { amx = mkp(-inf<ll>, -1), amn = mkp(inf<ll>, -1), amx_zero = mkp(-inf<ll>, -1), amn_zero = mkp(inf<ll>, -1); seg1_added = false; }void rsz(int _sz) { sz = _sz; }void work(ll *X, ll *Y) { rep(i, 1, n) idx[mkp(X[i], Y[i])] = i; rep(i, 1, n) if (Y[i]) ::pos[++tot] = db(X[i]) / db(Y[i]);if (tot) {sort(pos + 1, pos + tot + 1);tot = unique(pos + 1, pos + tot + 1) - pos - 1;}}void rst() { seg1_added = false; build(1, 1, tot); }void add(ll a, ll b) {auto _id = idx[mkp(a, b)];chkmax(amx, mkp(a, _id)), chkmin(amn, mkp(a, _id));if (!b) {auto _id = idx[mkp(a, b)];chkmax(amx_zero, mkp(a, _id)), chkmin(amn_zero, mkp(a, _id));}change1(1, 1, tot, Line(a, b));seg1_added = true;change2(1, 1, tot, Line(a, b));}ll query_max(ll x, ll y) {if (!seg1_added) return -inf<ll>;if (!y) {if (x >= 0) return x * amx.first;else return x * amn.first;}int p = lower_bound(pos + 1, pos + tot + 1, db(x) / y) - pos;Line tans;tans.k = inf<ll>;if (y >= 0) query1(1, 1, tot, p, tans);else query2(1, 1, tot, p, tans);return tans.k * x + tans.b * y;}pair<ll, int> query_max_with_id(ll x, ll y) {if (!seg1_added) return mkp(-inf<ll>, -1);if (!y) {if (x >= 0) return mkp(x * amx.first, amx.second);else return mkp(x * amn.first, amn.second);}int p = lower_bound(pos + 1, pos + tot + 1, db(x) / y) - pos;Line tans;tans.k = inf<ll>;if (y > 0) query1(1, 1, tot, p, tans);else query2(1, 1, tot, p, tans);pair<ll, int> curans = mkp(tans.k * x + tans.b * y, idx[mkp(tans.k, tans.b)]);if (x >= 0) {if (amx_zero.first != -inf<ll>) chkmax(curans, mkp(x * amx_zero.first, amx_zero.second));} else {if (amn_zero.first != inf<ll>) chkmax(curans, mkp(x * amn_zero.first, amn_zero.second));}return curans;}ll query_min(ll x, ll y) {return -query_max(-x, -y);}pair<ll, int> query_min_with_id(ll x, ll y) {auto ret = query_max_with_id(-x, -y);return mkp(-ret.first, ret.second);}
};segtree_cht T;signed main() { read(n); T.rsz(n);iota(fa + 1, fa + n + 1, 1);dsu_con_cnt = n;rep(i, 1, n) read(a[i]);rep(i, 1, n) read(b[i]);T.work(a, b);while(dsu_con_cnt > 1) {bool flag = false;rep(i, 1, n) cons[i].clear();rep(i, 1, n) cons[find(i)].eb(i);fill(val + 1, val + n + 1, inf<ll>);fill(rcs + 1, rcs + n + 1, mkp(0, 0));T.rst();rep(u, 1, n) {for(auto v : cons[u]) {auto [tv, w] = T.query_min_with_id(a[v], b[v]);// dbg(u, v, w, tv);if (val[u] > tv) val[u] = tv, rcs[u] = mkp(v, w);}for (auto v : cons[u]) T.add(a[v], b[v]);}T.rst();per(u, n, 1) {for(auto v : cons[u]) {auto [tv, w] = T.query_min_with_id(a[v], b[v]);// dbg(u, v, w, tv);if (val[u] > tv) val[u] = tv, rcs[u] = mkp(v, w);} for (auto v : cons[u]) T.add(a[v], b[v]);}rep(i, 1, n) {auto [u, v] = rcs[i];if (!u) continue;if (merge(u, v)) {flag = true;ans += val[i];}}if (!flag) break;}writeln(ans);#if defined(LOCAL) && !defined(CPH)std::cerr << "Spend Time : " << clock() * 1. / CLOCKS_PER_SEC * 1e3 << " ms \n";
#endifreturn 0;
} 

后来发现我的想法和maspy的基本相同,而李超树写法好像比maspy的CHT板子跑的快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/808104.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

30. 协程

1.协程的概念 1.1 定义 进程是操作系统内部运行的程序 线程是进程内部运行的程序 协程是线程内部运行的程序 协程是单线程下的并发,又成微线程,英文名coroutine 1.2 协程的优点协程切换的开销更小 GIL锁导致同一时刻只能运行一个线程,一个线程内不会限制协程数,单线程就可以…

.net core 安装服务

https://www.jianshu.com/p/e1b3b61f876a使用NSSM 后面的代码演示以Asp.net Core 2.1作为演示,其他.Net Core方式一致。 1、确保.Net Core程序可以正常运行 先把Asp.net Core发布,然后直接运行dotnet命令,确保程序可以运行并访问 2、使用NSSM安装dotnet 下载NSSM,使用命…

vs2015安装包丢失或损坏解决工具 或者不能启动

打开“本地组策略编辑器”(gpedit.msc)。展开“计算机配置”>“管理模板”>“系统”>“Internet 通信管理”,然后选择“Internet 通信设置”。选择“关闭自动根证书更新”>,“禁用”,然后选择“确定”或“应用”。  下载最新的组件版本(备份的) https://lea…

uboot 启动自编写程序的方式

uboot 启动自编写程序的方式 uboot 存在 boot 命令。 自己最初在尝试撰写串口程序时,选择了使用汇编来完成。 在这段时间,自己使用 go 命令来尝试载入程序 先是在 Ubuntu 上搭建 tftp 目录 # /etc/default/tftpd-hpaTFTP_USERNAME="tftp" TFTP_DIRECTORY="/ho…

10.Java集合框架_List接口

集合与数组的区别数组:长度开始时必须指定,而且一旦指定,不能修改。 保存的必须为同一类型的元素。 使用数组进行增加/删除元素比较麻烦。集合:可以动态保存任意多个对象,使用比较方便。 提供了一系列方便操作对象的方法: add、remove、set、get。 使用集合添加,删除新元…

20240924

[牛半仙的妹子 Tree(tree)](http://ac.robo-maker.cn/d/contest/p/ZY1044?tid=66f28cd11bca2159e88c8fb0) 我们会发现其实牛半仙发癫时就等于将以前的标记清空,从头开始,所以我们可以考虑根号分治,如果两个牛半仙发癫的时间间隔小于 \(\sqrt n\) ,那么我们可以直接暴力枚举两…

『模拟赛』冲刺CSP联训模拟2

『模拟赛记录』冲刺CSP联训模拟2Rank 不重要了A. 挤压 你说的对,期望怎么能算签呢? 一个重要的性质:一个数的平方可以在二进制下表示为 \(\sum_{i,j}\ s_i\ s_j\ 2^{i+j}\),所以就可以分别求每一位对答案的贡献了。 设 \(f_{i,1/0,1/0}\) 表示到第 \(i\) 个数我们枚举的两位…

PbootCms上传图片变模糊、上传图片尺寸受限的解决方案

在使用PbootCMS的过程中,如果上传的图片被压缩变得模糊,通常是因为上传的图片尺寸过大。PbootCMS 默认的上传图片限制宽度为 1920 像素,缩略图的限制大小为 10001000 像素。可以通过调整这些参数来解决这个问题。 解决方案打开 config.php 文件 调整 max_width 和 max_heigh…

ROS基础入门——实操教程

ROS新人可看ROS基础入门——实操教程前言 本教程实操为主,少说书。可供参考的文档中详细的记录了ROS的实操和理论,只是过于详细繁杂了,看得脑壳疼,于是做了这个笔记。Ruby Rose,放在这里相当合理前言:本文初编辑于2024年10月24日 CSDN主页:https://blog.csdn.net/rvdgds…

PbootCMS增加可允许上传文件类型,例如webp、mov等文件格式扩展

在PbootCMS中增加可允许上传的文件类型(例如 webp、mov 等文件格式),需要在多个地方进行配置。以下是详细的步骤: 操作步骤 1. 修改 config.php 文件 首先需要修改 config.php 文件,增加允许上传的文件类型。打开 config.php 文件打开 config.php 文件,通常位于 /config …

出现“登录失败,表单提交校验失败”,请检查服务器环境

如果出现“登录失败,表单提交校验失败”,请检查服务器环境,然后刷新页面重试,或者删除 runtime 文件夹,然后刷新页面重试。 操作步骤删除 runtime 文件夹使用 FTP 客户端或 SSH 连接到服务器。 删除 runtime 文件夹:bashcd /path/to/your/site rm -rf runtime刷新页面清除…

多次密码错误导致登录界面锁定,可以删除网站的 runtime 文件夹

如果多次密码错误导致登录界面锁定,可以删除网站的 runtime 文件夹,然后刷新页面重试。 操作步骤删除 runtime 文件夹使用 FTP 客户端或 SSH 连接到服务器。 删除 runtime 文件夹:bashcd /path/to/your/site rm -rf runtime刷新页面清除浏览器缓存。 重新访问后台登录页面扫…