返回「邏輯與算法」主頁
本页面收录难以归类的知识点。对于公式和定理,另见 邏輯與算法:柿子 。
带权中位数
带权中位数 :给定数组 W,X,保证 Wi≥0。求 kargmini=1∑NWi∣k−Xi∣。
先考虑不带权的,那么带权就是 Xi 出现了 Wi 次。按 Xi 升序排序,顺序累加 Wi,找到第一个使得 Wi 的前缀和 ≥21i=1∑NWi 的位置 m,则最优解为 k∗=Xm。
例 1:N 个人围成一圈,第 i 个人初始有 Ai 个物品。i 与 (i+1)modN 每传递 1 个物品的代价为 Bi。欲通过若干次传递使每个人手中的物品数量相同,求最小总代价。
设 di=Ai−Aˉ。设 i 传递给 (i+1)modN 了 ci 个(可能为负)。根据物品守恒,ci=cN+Di。则总传递代价 Ans(cN)=i=1∑NBi∣ci∣=i=1∑NBi∣cN+Di∣。欲构造 cN 以最小化 Ans(cN),这就是带权中位数问题。
例 2:序列 A,B,设有理数序列 c,d 满足 (Ai+ci)(Bj+dj)=(Aj+cj)(Bi+di),最小化 ∑∣ci∣+∣di∣。(ABC479G)
要么 ci≡0,要么 di≡0。以 ci≡0 为例,设 Bi+di=kAi,需最小化 ∑∣di∣=∑∣kAi−Bi∣=∑∣Ai∣k−AiBi,这就是带权中位数问题。注意特判非零的 A 为空。
将 (Ai,Bi) 看作二维坐标系的点,将每个点移动到同一条过原点的直线,代价是 ∣ci∣+∣di∣。当 ∣k∣⩽1 时,点纵向移动不劣,∣k∣>1 时,横向不劣。
排名离散化
离散化 (Discretization):把连续的大范围的东西转换成离散的小范围排名
1 2 3 4
| auto g = a; rgs::sort(g); g.erase(rgs::unique(g).begin(), g.end()); for (auto& x : a) x = rgs::lower_bound(g, x) - g.begin();
|
例 1 (全平面的极角离散化)
例 2 (ABC481F):N 点 M 边有向图,边权 Wi∈[1,9]。求从 1 到每个点 v 的所有路径中,边权序列所表示的大整数最小的路径。
先最小化路径长度,路径长度相同时,大整数比较即字典序比较。实现时用 BFS 分层,同一层的节点一起转移,先 f(v)←10f(u)+c,再收集所有 f(v) 并离散化。复杂度 O(MlogN)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| dep[0] = 0; layer[0].push_back(0); for (int d = 0; d < N; d++) { vector<int> g; for (auto y : layer[d]) g.push_back(dis[y]); ranges::sort(g); g.erase(ranges::unique(g).begin(), g.end()); for (auto y : layer[d]) dis[y] = ranges::lower_bound(g, dis[y]) - g.begin(); for (auto x : layer[d]) { for (auto [y, c] : adj[x]) { if (dep[y] == -1) { dep[y] = dep[x] + 1; layer[dep[y]].push_back(y); } if (dep[y] != dep[x] + 1) { continue; } if (dis[y] > 10 * dis[x] + c) { dis[y] = 10 * dis[x] + c; ans[y] = 10 * ans[x] + c; } } } }
|
例 3 (2024 TOPC L):N 点 M 边有向图,边权 Wi。求从 S 到 T 恰好经过 K 条边的所有路径中,边权序列字典序最小的路径,输出 hash 值 ∑i=1KWeiXK−i。
倍增预处理,设 fk,u,v:从 u 到 v,长度为 2k,字典序最小的路径序列。拼凑得到 K。
考虑 merge。传统做法是 fa+b,u,v=minw(fa,u,w+fb,w,v),但不可直接维护序列,因而分别维护字典序和 hash 值。为比较字典序,同一批转移时先 ra+b,u,v←ra,u,wN2+rb,w,v,再离散化,其中 +∞ 表示不可达。对于 hash 值,ha+b,u,v=ha,u,w∗Xb+hb,w∗,v。复杂度 O(N3logK)。
排序不等式
排序不等式 :只改变配对方式,
- ∑AiBπ(i):同序最大,逆序最小
- ∑∣Ai−Bπ(i)∣:同序最小,逆序最大
- ∑(Ai−Bπ(i))2:同序最小,逆序最大
例 :序列 A,B,其中 ∣A∣⩽∣B∣,选取 B 的任一 ∣A∣ 元子列的任一排列 b,最大化 ∑∣Ai−bi∣。(CF1921D)
不妨设 A 递增。由排序不等式,b 递减,将 B 倒序排序,再选取一个递减子列 b。而且 b 是 B 的某前缀与某后缀拼接起来的。枚举分界点计算答案。O(NlogN)。
区间包含树
区间包含树 :给定若干区间 [Li,Ri),要么互不相交(Ri≤Lj),要么一个包含另一个(Li≤Lj<Rj≤Ri),不存在交叉重叠(Li<Lj<Ri<Rj),构造区间包含树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| vector<vector<int>> build(const vector<pair<int, int>>& seg) { const int N = int(seg.size()); struct Seg { int l, r, id; }; vector<Seg> A(N + 1); constexpr int inf = 0x3f3f3f3f; A[0] = {-inf, inf, 0}; for (int i = 0; i < N; i++) { auto [l, r] = seg[i]; A[i + 1] = {l, r, i + 1}; } ranges::sort(A, {}, [](const Seg& x) { return pair(x.l, -x.r); }); vector<int> s; vector<vector<int>> adj(N + 1); for (int i = 0; i <= N; i++) { while (!s.empty() && A[s.back()].r <= A[i].l) { s.pop_back(); } if (!s.empty()) { adj[A[s.back()].id].push_back(A[i].id); } s.push_back(i); } return adj; }
|
RSA算法
Key Generation
- Two distinct large prime numbers p and q
- Modulus n=p×q
- Public Exponent e : 1<e<ϕ(n) and e⊥ϕ(n)
- Private Exponent d : d×e≡1(modϕ(n))
Result
- Public Key: (e,n) - distributed to everyone
- Private Key: (d,n) - kept secret
Encryption (Confidentiality)
- Suppose Alice wants to send an integer m (where 0≤m<n) to Bob
- Encryption: Alice computes c≡me(modn) using Bob’s public key (e,n)
- Transmission: Bob receives the ciphertext c
- Decryption: Bob computes m≡cd(modn) using his private key (d,n)
- Obviously, m≡cd≡med≡mkϕ(n)+1≡m(modn) for some integer k
树点边容斥
树点边容斥 :设树 T=(V,E),设计数对象 S 对应一个非空顶点集合 A(S)⊂V,且 T[A(S)] 连通,那么 ∣S∣=∑v∈V#{S:v∈A(S)}−∑e=uv∈E#{S:u,v∈A(S)}。也就是 #{ 对象 }=∑v#{ 经过 v 的对象 }−∑e#{ 经过 e 的对象 }。
引理:树有 ∣V∣−∣E∣=1。
理解:设 T[A(S)]=(V(S),E(S)),这是一棵树,从而有 1=∣V(S)∣−∣E(S)∣。把两项分别写成指示函数,1=∑v∈V[v∈A(S)]−∑e=uv∈E[u,v∈A(S)],对所有对象 S∈S 求和,∣S∣=∑S∈S∑v∈V[v∈A(S)]−∑S∈S∑e=uv∈E[u,v∈A(S)],交换求和次序后即得结论。
推论 :设若干区间 Ii=[li,ri] 满足 li<li+1,ri<ri+1,求区间 J=[L,R] 的数量,满足 J 至少包含一个 Ii。设 Si 表示区间 [L,R] 完整覆盖 Ii,即 Si={[L,R]:L≤li, R≥ri},所求即
i⋃Si=i∑∣Si∣−i∑∣Si∩Si+1∣
理解:设区间 [L,R] 完整覆盖了 k 个 Ii,那么第一项计数 k 次,第二项减去 k−1 次,最终只计数一次。
异或线性基
求子集的数量,满足异或和为 0。答案为 2N−rank。
正则括号串
正则括号串 (Regular Bracket Sequence, RBS):
- 生成方式:连接与加括号
- 前缀和保持非负
- 一一对应于有序树 T=(T1T2…Tk)
- 一一对应于有序二叉树 T=(TL)TR
——CuteCube
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| vector<vector<int>> build(string S) { S = "(" + S + ")"; vector<vector<int>> adj; stack<int> s; for (auto c : S) { if (c == '(') { int x = int(adj.size()); if (!s.empty()) { adj[s.top()].push_back(x); } s.push(x); adj.emplace_back(); } else { s.pop(); } } return adj; }
string restore(const vector<vector<int>>& adj) { string S; auto dfs = [&](this auto&& self, int x) -> void { S += '('; for (auto y : adj[x]) { self(y); } S += ')'; }; for (auto y : adj[0]) { dfs(y); } return S; }
|
例 1:删除 BS S 至多 K 个字符,最小化最长 RBS 子序列。(CF2233C)
先做一遍括号匹配,最后希望剩下 )))((,右括号与左括号有个分界点。重点是绝对不存在一对匹配的括号,左括号在左半区,右在右半区,所以所有匹配的括号对要么全在左半,要么全在右半。左半的括号对删左,右半删右。如此最优。
例 2:RBS S,任意操作:选择两个不交的 RBS 子串,并交换位置。判断最终是否可以变换为 T。(CF2210D)
在树上做,操作等价于:选两个没有祖先关系的点 x,y,将 x 的若干子节点的子树与 y 的做交换。不变量:叶子节点数;从根到第一个分叉节点之间的节点数。