1-杂项

1276 字
6 分钟
1-杂项

一些杂项Trick

区间合并#

using pii = pair<int, int>;
vector<pii> a, res;
sort(a.begin(), a.end());
auto [st, ed] = a[0];
for (int i = 1; i < a.size(); ++i) {
auto [st1, ed1] = a[i];
if (st1 <= ed) {
ed = max(ed, ed1);
} else {
res.push_back({st, ed});
st = st1;
ed = ed1;
}
}
res.push_back({st, ed});

读题#

取模#

998244353和1e9 + 7都是质数

998244353适合用于做FFT和NTT变换

灵感#

  1. 代码上串行,如果数据互相独立可视作并行
  2. 第k个都是实虚指针
  3. 移动隔板可看作合并空白区间

取整#

通用#

f(x)=f(x)\lceil f(x) \rceil = -\lfloor -f(x) \rfloor

由于C++不是统一向下取整而是向0取整所以没什么用

离散加小步长#

需要单调

f(x)=f(x1)+1(当 0<f(x)f(x1)1)\lceil f(x) \rceil = \lfloor f(x-1) \rfloor +1 \quad (\text{当 } 0 < f(x)-f(x-1) \le 1)

向下取整和向上取整#

向下取整是闭开

向上取整是开闭

1+log2(x)1 + \lfloor log_2(x)\rfloorlog2(x+1)\lceil log_2(x + 1) \rceil在整数域上由于向下取整和向上取整对应的区间中总是在同一直线,同时向下取整的左端点总是小于向上取整的左端点,向下取整的右端点总是大于向上取整的右端点,故两者在整数域上等效,同时1+log2(x)bit_width(x)1 + \lfloor log_2(x)\rfloor \Leftrightarrow bit\_width(x)

中点#

中点m取n的时候,偶数n,m在偏右,如遍历左边取到m的话,奇偶都是左边偏大,取n - 1的时候,奇数左偏大,偶数左右相等

中点m由n时,

  1. 偶数n,m偏右中点,遍历左边取m的话,左偏大,不取的话,一样大。
  2. 奇数n,m不偏不倚,遍历左边取m的话,左偏大,不取的话,右偏大

取的话

左[l, m],右[m + 1, r]

不取的话

左[l, m - 1], 右[m, r]

中点m由n-1时

  1. 偶数n,m偏左中点,遍历左边取m的话,一样大,不取的话,右偏大
  2. 奇数n,m不偏不倚,遍历左边取m的话,左偏大,不取的话,右偏大

取的话

左[l, m],右[m + 1, r]

不取的话

左[l, m - 1],右[m, r]

和中点m由n时的取不取情况是镜像的,但是表达式只取决于左边取不取中点

n12\lfloor \frac{n-1}{2} \rfloor能把1,2,3,4,5,6,1,2,3,4,5,6,\ldots映射到0,0,1,1,2,2,0,0,1,1,2,2,\ldots

循环#

序列#

主要运算对象,

左指针i右指针j区间长度l
j - i + 1
j - i
j - i
j - i - 1

一般常用

i=jl+1,i=jli = j - l + 1, i = j - lj=i+l1,j=i+lj = i + l - 1, j = i + l

do-while#

即循环体至少执行一次。

场景 1:初始状态不合法,必须先动

快排。i = l-1 在数组外面,不能先判断 q[i],必须先 ++i。

场景 2:提取数字的每一位

// n = 0 时也要输出 "0",至少执行一次
do {
digits.push_back(n % 10);
n /= 10;
} while (n > 0);

如果用 while (n > 0),n = 0 时一位都不会提取。

场景 3:输入校验 / 重试

do {
cin >> x;
} while (x < 1 || x > 100); // 至少读一次,不合法就重来

场景 4:至少比较一次的迭代

// 找到下一个不同的元素
do { ++j; } while (j < n && a[j] == a[i]);

总结

whiledo-while
先判断,可能一次都不执行先执行,至少跑一次
初始状态已合法初始状态不合法 / 必须先动

配置环境#

学校的破烂电脑扩展不更新的话Intelligence直接失效,没网的话要用Vscode只能用Code Runner

Tasks.json#

必要的就type, label, command, args

tasks:[
{
"type" : "cppbuild",
"label" : "build",
"command" : "g++",
"args" : [
"-g",
"${file}",
"-o",
"${workspaceFolder}/bin/${fileBaseNameNoExtension}.exe"
]
}
]

性能相关#

一些卡常的性能细节

IO#

  1. ios::sync_with_stdio(false);cin.tie(nullptr)开了后cin的性能追平scanf(),可以用cin.tie(nullptr)->sync_with_stdio(false)
  2. endle要重新刷新缓冲区,性能不如\n

数据编排#

SOA在SIMD优化下比AOS会好一些

离散化#

分两种,一种哈希,一种二分

哈希#

哈希适合需要编码的数据

struct discretization {
unordered_map<string, int> m;
discretization(const vector<string>& a) {
for (int i = 0; i < a.size(); ++i) {
m[a[i]] = i;
}
}
int operator[](string x) {
return m[x];
}
} dt;

二分#

二分适合数值大数量小的数据

struct discretization {
vector<int> help;
discretization(const vector<int>& a) : help(a) {
sort(help.begin(), help.end());
help.erase(unique(help.begin(), help.end()));
}
int operator[](int x) {
return ( lower_bound(help.begin(), help.end(), x) - help.begin() );
}
}

元素迁移到完全二叉树上#

设完全二叉树元素数量为m=2n1+cm = 2^n - 1 + c,要扩展到满二叉树,其中n为1-base的满足c0c \ge 0的最大层值,即m<2n+11+cm < 2^{n + 1} - 1 + c

2n1m<2n+112^n - 1 \le m < 2^{n + 1} - 1,如果元素1-base的话那就是从上一层最后一个元素到该层的非最后一个元素

n=floor(log2(m+1))n = floor(log_2(m + 1))

假如是满足c>0c > 0而不是c0c \ge 0,则有2n1<m2n+112^n - 1 < m \le 2^{n + 1} - 1,即2nm<2n+12^n \le m < 2^{n + 1},1-base的话就是从该层第一个元素到该层最后一个元素。

n=floor(log2(m))n = floor(log_2(m)), 理论上来讲n=floor(log2(m))n = floor(log_2(m)),需要开1<<(n+1)=1<<(floor(log2(m)+1)1 << (n + 1) = 1 << (floor(log_2(m) + 1)1<<bit_width(m)1 << bit\_width(m)大小的数组

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

1-杂项
https://skaco2.com/posts/01-algorithm/1-杂项/
作者
SKACO2
发布于
2026-04-02
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
SKACO2
Hello……
公告
欢迎来到我的博客!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
53
分类
8
标签
54
总字数
58,255
运行时长
0
最后活动
0 天前

目录