加载中...
✦ 正在前往提瓦特 ✦
✦ 我叫 Arlecchino 愚人众执行官第四席
Arlecchino

不常见的好用函数

【计算】ratio

只能存静态,但无精度误差。

示例代码
#include <iostream>
#include <ratio>
#include <boolalpha>
using namespace std;
// OI场景:求两个静态分数的和并约分,判断是否等于目标分数
int main() {
    // 竞赛中常见的静态分数(比如题目给出的固定比例)
    using r1 = ratio<12, 18>;  // 题目给定的分数1:12/18
    using r2 = ratio<5, 9>;    // 题目给定的分数2:5/9
    
    // 编译期求和+自动约分(无需手写gcd约分)
    using sum = ratio_add<r1, r2>;
    cout << "12/18 + 5/9 = " << sum::num << "/" << sum::den << endl; // 输出11/9
    
    // 编译期比较:判断和是否等于11/9(避浮点比较陷阱)
    using target = ratio<11, 9>;
    cout << "和是否等于11/9:" << boolalpha << ratio_equal<sum, target>::value << endl;
    
    return 0;
}// 豆包生成

【查询】adjacent_find

基于 #include<algorithm>,用于查找搜索相等或满足指定条件的两个相邻元素。是 \(O\left(n\right)\)的。

查找相等俩元素的示例代码
#include <iostream>
#include <vector>
#include <algorithm> // 必须包含该头文件
using namespace std;

int main() {
    // OI中常见的数组/vector场景
    vector<int> a = {1, 2, 2, 3, 3, 3, 4};
    
    // 查找第一个相邻相等的元素对
    auto it = adjacent_find(a.begin(), a.end());
    
    if (it != a.end()) {
        // 输出:找到的元素值为2,下标为1(OI中常需要下标)
        cout << "找到第一个相邻相等的元素:" << *it 
             << ",下标:" << it - a.begin() << endl;
    } else {
        cout << "无相邻相等的元素" << endl;
    }
    return 0;
}
查找指定条件的相邻元素的示例代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> a = {1, 2, 3, 2, 4, 1};
    
    // 自定义条件:相邻两个元素的和等于5
    auto it = adjacent_find(a.begin(), a.end(),
        [](int x, int y) { return x + y == 5; });
    
    if (it != a.end()) {
        cout << "找到第一个和为5的相邻对:" << *it << " 和 " << *(it+1)
             << ",下标:" << it - a.begin() << endl;
    } else {
        cout << "无满足条件的相邻对" << endl;
    }
    return 0;
}
判断连续块数量的示例代码
int count_adjacent_blocks(vector<int>& a) {
    if (a.empty()) return 0;
    int cnt = 1; // 至少有1个块
    auto it = a.begin();
    while ((it = adjacent_find(it, a.end())) != a.end()) {
        cnt++;
        it++; // 跳过当前重复对,找下一个
    }
    return cnt;
}

【计算】accumulate

基于 #include<numeric>,对容器指定范围内的元素执行累积操作—— 默认是“累加求和”,也可通过自定义实现任意逻辑(比如乘积、统计条件元素、拼接字符串等)。是 \(O\left(n\right)\)的。

求和示例代码
#include <iostream>
#include <vector>
#include <numeric> // 必须包含该头文件!
using namespace std;

int main() {
    // OI中常见的整数数组求和
    vector<int> a = {1, 2, 3, 4, 5, 6};
    // 初始值0,累加区间[begin, end)的所有元素
    int sum = accumulate(a.begin(), a.end(), 0);
    cout << "数组和:" << sum << endl; // 输出21

    // 进阶:大数求和(OI中避免溢出,初始值用long long)
    vector<long long> b = {1e18, 1e18, 1e18};
    long long big_sum = accumulate(b.begin(), b.end(), 0LL); // 初始值0LL(long long)
    cout << "大数和:" << big_sum << endl; // 输出3e18
    return 0;
}
自定义乘法示例代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int main() {
    vector<int> a = {2, 3, 4, 5};
    // 初始值1(乘积的单位元),自定义乘积逻辑
    int product = accumulate(a.begin(), a.end(), 1,
        [](int res, int x) { return res * x; }); // res=累积结果,x=当前元素
    cout << "数组乘积:" << product << endl; // 输出120
    return 0;
}
统计类示例代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int main() {
    vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8};
    
    // 场景1:统计偶数的和
    int even_sum = accumulate(a.begin(), a.end(), 0,
        [](int res, int x) {
            return res + (x % 2 == 0 ? x : 0); // 偶数加x,奇数加0
        });
    cout << "偶数和:" << even_sum << endl; // 输出2+4+6+8=20

    // 场景2:统计奇数的个数
    int odd_cnt = accumulate(a.begin(), a.end(), 0,
        [](int res, int x) {
            return res + (x % 2 == 1 ? 1 : 0); // 奇数计数+1,偶数+0
        });
    cout << "奇数个数:" << odd_cnt << endl; // 输出4
    return 0;
}
拼接字符串示例代码
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
using namespace std;

int main() {
    vector<string> strs = {"OI", " ", "竞赛", " ", "加油!"};
    // 初始值为空字符串,自定义拼接逻辑
    string res = accumulate(strs.begin(), strs.end(), string(""),
        [](string s, string t) { return s + t; });
    cout << "拼接结果:" << res << endl; // 输出“OI 竞赛 加油!”
    return 0;
}

【构造】adjacent_difference

对源容器的元素计算相邻元素的运算结果并存储到目标容器中,默认是后一个元素 - 前一个元素(即构建差分数组),也可通过自定义二元函数实现任意相邻运算(比如和、乘积、异或等)。是 \(O\left(n\right)\)的。

构建差分数组示例代码
#include <iostream>
#include <vector>
#include <numeric> // 必须包含该头文件!
using namespace std;

int main() {
    // OI中常见的原数组
    vector<int> a = {1, 3, 5, 7, 9};
    // 目标容器(差分数组),大小和原数组一致
    vector<int> diff(a.size());

    // 构建差分数组(默认运算:后-前)
    adjacent_difference(a.begin(), a.end(), diff.begin());

    // 输出结果:diff[0]=1, diff[1]=2, diff[2]=2, diff[3]=2, diff[4]=2
    cout << "原数组:";
    for (int x : a) cout << x << " ";
    cout << "\n差分数组:";
    for (int x : diff) cout << x << " ";
    cout << endl;
    return 0;
}

【构造】accumulate

核心作用是对容器指定区间的元素做累积运算,\(O\left(n\right)\)。

求和示例代码
#include <iostream>
#include <vector>
#include <numeric> // 必须是这个头文件!
using namespace std;
using ll = long long; // OI中常用typedef简化长整型

int main() {
    // 场景1:int数组求和(基础)
    vector<int> a = {1, 2, 3, 4, 5};
    int sum_int = accumulate(a.begin(), a.end(), 0); // 初始值0(int)
    cout << "int数组和:" << sum_int << endl; // 输出15

    // 场景2:大数求和(避免溢出,关键!)
    vector<ll> b = {1e18, 2e18, 3e18};
    ll sum_ll = accumulate(b.begin(), b.end(), 0LL); // 初始值0LL(ll类型)
    cout << "long long数组和:" << sum_ll << endl; // 输出6e18

    return 0;
}
满足条件的元素个数 / 和示例代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int main() {
    vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    // 场景1:统计偶数的个数
    int even_cnt = accumulate(a.begin(), a.end(), 0,
        [](int res, int x) { return res + (x % 2 == 0 ? 1 : 0); });
    cout << "偶数个数:" << even_cnt << endl; // 输出4

    // 场景2:统计大于5的元素和
    int gt5_sum = accumulate(a.begin(), a.end(), 0,
        [](int res, int x) { return res + (x > 5 ? x : 0); });
    cout << "大于5的元素和:" << gt5_sum << endl; // 输出6+7+8+9=30

    return 0;
}
对结构体数组的某个属性求和(比如所有物品的价格)示例代码
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
using namespace std;

// OI模拟题常见结构体:物品
struct Item {
    string name;
    int price; // 价格
    int cnt;   // 数量
};

int main() {
    vector<Item> items = {
        {"苹果", 5, 10},
        {"香蕉", 3, 20},
        {"橙子", 4, 15}
    };

    // 统计所有物品的总金额(价格*数量)
    int total_money = accumulate(items.begin(), items.end(), 0,
        [](int res, const Item& item) {
            return res + item.price * item.cnt;
        });
    cout << "总金额:" << total_money << endl; // 5*10+3*20+4*15=170

    return 0;
}
</details>

<details>
<summary>子区间求和(前缀和)示例代码</summary>

```cpp
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int main() {
    vector<int> a = {10, 20, 30, 40, 50, 60};
    // 求下标2~4的和(即30+40+50),对应区间[begin+2, begin+5)
    int sub_sum = accumulate(a.begin() + 2, a.begin() + 5, 0);
    cout << "下标2~4的和:" << sub_sum << endl; // 输出120

    return 0;
}

注:与 exclusive_scan(排他型)前缀和函数不同。

【构造】iota

从指定初始值开始,为容器的指定区间依次赋「递增 \(1\)」的连续值(默认步长为 \(1\)),\(O\left(n\right)\)。

生成0到n-1的下标数组、1到n的编号数组示例代码
#include <iostream>
#include <vector>
#include <numeric> // 必须包含该头文件!
using namespace std;

int main() {
    // 场景1:生成0~4的连续整数(下标数组)
    vector<int> idx(5);
    iota(idx.begin(), idx.end(), 0); // 初始值0,依次赋0,1,2,3,4
    cout << "下标数组:";
    for (int x : idx) cout << x << " "; // 输出0 1 2 3 4
    cout << endl;

    // 场景2:生成1~5的连续整数(编号数组)
    vector<int> num(5);
    iota(num.begin(), num.end(), 1); // 初始值1,依次赋1,2,3,4,5
    cout << "编号数组:";
    for (int x : num) cout << x << " "; // 输出1 2 3 4 5
    cout << endl;

    return 0;// 字符也可以
}
初始化并查集父节点示例代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

// OI中并查集模板(简化版)
struct DSU {
    vector<int> parent;
    DSU(int n) {
        parent.resize(n);
        // 核心:用iota初始化父节点为自身(0~n-1)
        iota(parent.begin(), parent.end(), 0);
    }
    int find(int x) {
        return parent[x] == x ? x : parent[x] = find(parent[x]);
    }
};

int main() {
    DSU dsu(5);
    cout << "并查集初始父节点:";
    for (int x : dsu.parent) cout << x << " "; // 输出0 1 2 3 4
    cout << endl;

    return 0;
}

【构造】transform

基于 #include<algorithm>,对一个或两个输入容器的元素执行自定义转换/运算,将结果写入输出容器(或原地修改原容器),\(O\left(n\right)\)。

批量处理单个容器元素示例代码
#include <iostream>
#include <vector>
#include <algorithm> // 必须包含该头文件!
#include <cctype>    // toupper需要
using namespace std;

int main() {
    // 场景1:数组元素全部乘2
    vector<int> a = {1, 2, 3, 4, 5};
    vector<int> res1(a.size()); // 输出容器大小和输入一致
    transform(a.begin(), a.end(), res1.begin(),
        [](int x) { return x * 2; }); // 一元函数:x*2
    cout << "元素乘2:";
    for (int x : res1) cout << x << " "; // 输出2 4 6 8 10
    cout << endl;

    // 场景2:数组元素取绝对值(处理负数)
    vector<int> b = {-1, -2, 3, -4};
    transform(b.begin(), b.end(), b.begin(), // 原地修改(输出迭代器=输入迭代器)
        [](int x) { return abs(x); });
    cout << "原地取绝对值:";
    for (int x : b) cout << x << " "; // 输出1 2 3 4
    cout << endl;

    // 场景3:字符串转大写(模拟题高频)
    string s = "oi competition";
    transform(s.begin(), s.end(), s.begin(),
        [](char c) { return toupper(c); }); // toupper转大写
    cout << "字符串转大写:" << s << endl; // 输出OI COMPETITION

    return 0;
}
对两个容器的对应元素执行运算示例代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;

int main() {
    vector<int> a = {1, 2, 3};
    vector<int> b = {4, 5, 6};
    vector<int> res(a.size());

    // 场景1:对应元素相加
    transform(a.begin(), a.end(), b.begin(), res.begin(),
        [](int x, int y) { return x + y; });
    cout << "对应元素相加:";
    for (int x : res) cout << x << " "; // 输出5 7 9
    cout << endl;

    // 场景2:对应元素异或(位运算题高频)
    transform(a.begin(), a.end(), b.begin(), res.begin(),
        [](int x, int y) { return x ^ y; });
    cout << "对应元素异或:";
    for (int x : res) cout << x << " "; // 输出5 7 5
    cout << endl;

    // 场景3:对应元素乘积模MOD(数论题高频)
    vector<ll> c = {1e9, 1e9 + 5};
    vector<ll> d = {1e9 + 7, 1e9 + 9};
    vector<ll> res_mod(c.size());
    transform(c.begin(), c.end(), d.begin(), res_mod.begin(),
        [](ll x, ll y) { return (x * y) % MOD; });
    cout << "乘积模MOD:";
    for (ll x : res_mod) cout << x << " "; // 输出0 45
    cout << endl;

    return 0;
}
从结构体数组中提取某个属性(比如所有物品的价格)示例代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

// OI模拟题常见结构体:物品
struct Item {
    string name;
    int price; // 价格
    int cnt;   // 数量
};

int main() {
    vector<Item> items = {
        {"苹果", 5, 10},
        {"香蕉", 3, 20},
        {"橙子", 4, 15}
    };
    vector<int> prices(items.size());

    // 提取所有物品的价格(一元函数访问结构体字段)
    transform(items.begin(), items.end(), prices.begin(),
        [](const Item& item) { return item.price; });
    cout << "物品价格列表:";
    for (int x : prices) cout << x << " "; // 输出5 3 4
    cout << endl;

    return 0;
}
结合 iota 生成自定义步长序列示例代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric> // iota需要
using namespace std;

int main() {
    // 生成0, 3, 6, 9, 12(步长3)
    vector<int> a(5);
    iota(a.begin(), a.end(), 0); // 先生成0~4
    transform(a.begin(), a.end(), a.begin(),
        [](int x) { return x * 3; }); // 乘3得到步长3序列
    cout << "步长3序列:";
    for (int x : a) cout << x << " "; // 输出0 3 6 9 12
    cout << endl;

    return 0;
}

【构造】generate_canonical

高精度生成 [0,1) 区间均匀分布浮点数。

生成 $0$ 到 $1$(左闭右开)高精度随机浮点数示例代码
#include <iostream>
#include <random>   // 必须包含该头文件!
#include <iomanip>  // setprecision需要(控制输出精度)
using namespace std;

int main() {
    // 步骤1:初始化随机数生成器(OI标准写法,避免rand()的缺陷)
    random_device rd; // 随机设备(获取真随机种子)
    mt19937 gen(rd()); // 梅森旋转算法生成器(URBG)

    // 步骤2:用generate_canonical生成[0,1)的double类型随机数
    // bits默认取double的有效位数(53),无需手动指定
    for (int i = 0; i < 5; i++) {
        double r = generate_canonical<double, 53>(gen);
        // 输出15位精度,体现高精度优势
        cout << "随机数[" << i+1 << "]:" << setprecision(15) << r << endl;
    }

    return 0;
}
生成 $a$ 到 $b$(左闭右开)区间的随机数示例代码
#include <iostream>
#include <random>
#include <iomanip>
using namespace std;

// 生成[a, b)区间的高精度随机浮点数(OI通用函数)
double random_double(double a, double b, mt19937& gen) {
    // [0,1) → [a,b):r*(b-a) + a
    return generate_canonical<double, 53>(gen) * (b - a) + a;
}

int main() {
    random_device rd;
    mt19937 gen(rd());

    // 生成[1.0, 10.0)区间的随机数
    for (int i = 0; i < 3; i++) {
        double r = random_double(1.0, 10.0, gen);
        cout << "区间[1,10)随机数[" << i+1 << "]:" << setprecision(10) << r << endl;
    }

    return 0;
}

【查询】大部分容器成员函数

begin 获取指向范围中第一个元素的迭代器。

cbegin 获取指向范围中第一个元素的 const 迭代器。

cend 获取 const 选定的范围末尾的 sentinel。

cdata 获取指向连续范围中第一个元素的 const 指针。

crbegin 获取指向范围开头的反向 const 迭代器。

crend 获取 crbegin() 返回结果末尾的 sentinel。

data 获取指向连续范围中第一个元素的指针。

empty 测试范围是否为空。

end 获取范围末尾的 sentinel。

rbegin 获取指向范围开头的反向迭代器。

rend 获取指向范围末尾的 sentinel 的反向迭代器。

size 获取范围的大小作为无符号值。

ssize 获取范围的大小作为带符号值。

【查询】regex_match

检查整个字符串是否完全符合正则表达式的规则(而非部分匹配),返回bool值表示“完全匹配”或“不匹配”。

warning:大数据时慎用,效率低于手动遍历。

验证 IPv4 地址的某一段是否合法$0\le x\le 255$ 示例代码
#include <iostream>
#include <string>
#include <regex>
using namespace std;

// 正则规则解释:
// ^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$ → 匹配0-255的数字
bool is_valid_ip_segment(const string& s) {
    regex reg("^(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");
    return regex_match(s, reg);
}

int main() {
    cout << boolalpha;
    cout << "255: " << is_valid_ip_segment("255") << endl; // true
    cout << "256: " << is_valid_ip_segment("256") << endl; // false
    cout << "192: " << is_valid_ip_segment("192") << endl; // true
    cout << "0: " << is_valid_ip_segment("0") << endl;     // true
    cout << "01: " << is_valid_ip_segment("01") << endl;   // true(若禁止前导零可调整正则)
    return 0;
}
与 regex_search 区别

完整匹配 vs 部分匹配

#include <iostream>
#include <string>
#include <regex>
using namespace std;

int main() {
    string s = "123abc";
    regex reg("\\d+"); // 匹配1个及以上数字(无^$,非完整匹配)

    // regex_match:整个字符串必须匹配 → 失败(有abc)
    cout << boolalpha << "regex_match: " << regex_match(s, reg) << endl; // false

    // regex_search:只要有子串匹配 → 成功(匹配到123)
    cout << "regex_search: " << regex_search(s, reg) << endl; // true

    // 若想regex_match成功,需修改正则为^\\d+.*$(完整匹配,允许数字后接其他字符)
    regex reg_full("^\\d+.*$");
    cout << "regex_match(完整匹配): " << regex_match(s, reg_full) << endl; // true

    return 0;
}

【修改】regex_replace

在目标字符串中,根据正则表达式匹配子串,并将匹配的子串替换为指定内容(支持替换所有匹配项,或仅替换第一个匹配项)。

warning:大数据时禁用,效率远低于手动遍历替换。

指令中的 “OI” 统一替换为 “竞赛”,支持匹配多字符子串示例代码
#include <iostream>
#include <string>
#include <regex>
using namespace std;

int main() {
    string s = "OI模拟赛 OI算法题 OI编程";
    // 正则规则:OI → 匹配子串"OI";替换为"竞赛"
    regex reg("OI");
    string res = regex_replace(s, reg, "竞赛");

    cout << "替换后:" << res << endl; // 竞赛模拟赛 竞赛算法题 竞赛编程

    return 0;
}
清洗字符串(比如隐藏数字)示例代码
#include <iostream>
#include <string>
#include <regex> // 必须包含该头文件!
using namespace std;

int main() {
    string s = "OI2026竞赛123题456";
    // 正则规则:\\d → 匹配单个数字;替换格式串:* → 把数字换成*
    regex reg("\\d"); 
    string res = regex_replace(s, reg, "*");

    cout << "原字符串:" << s << endl;       // OI2026竞赛123题456
    cout << "替换后:" << res << endl;      // OI****竞赛***题***

    return 0;
}

【修改】关于字符串常用

字符串转 int:stoi(s);

字符串转 long long:stoll(s);

字符串转 double:stod(s);

字符串转 float:stof(s);

字符串转 long:stol(s);

字符串转 long double:stold(s);

字符串转 unsigned long:stoul(s);

字符串转 unsigned long long:stoull(s);

拆解记忆 s-> string,to-> 英文to,...-> 对应的类型(如 stoi 中 i-> int)。

转字符串:to_string(n);

【构造】随机数

rand

的致命缺陷:

  • 随机性差:生成的数存在“周期性”,大数据量下分布不均;
  • 范围小:RAND_MAX 通常是 \(32767\),无法生成超过 \(32767\) 的随机数;
  • 精度低:转浮点数(rand()/RAND_MAX)仅能到万分之一级别,远低于 OI 需求;
  • 种子重复:若程序运行速度快(比如循环内调用 srand),会生成相同随机数。
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

// OI通用函数:生成[a, b]的随机整数
int rand_int(int a, int b) {
    return rand() % (b - a + 1) + a;
}

int main() {
    srand((unsigned)time(0));
    // 生成1~100的随机数
    for (int i = 0; i < 3; i++) {
        cout << rand_int(1, 100) << " "; // 输出如56 89 12
    }
    return 0;
}
mt19937(简记:茅台19937)

整数:uniform_int_distribution;

#include <iostream>
#include <random>  // 必须包含该头文件!
using namespace std;

int main() {
    // 步骤1:初始化随机数引擎(OI标准写法)
    random_device rd;  // 真随机数设备(获取种子)
    mt19937 gen(rd()); // 初始化mt19937引擎

    // 步骤2:定义分布器(生成[1, 100]的整数)
    uniform_int_distribution<int> dist(1, 100);

    // 步骤3:生成随机数(调用dist(gen))
    for (int i = 0; i < 5; i++) {
        int r = dist(gen);
        cout << "随机数[" << i+1 << "]:" << r << endl;
    }
    return 0;
}

高精度浮点数:uniform_real_distribution;

#include <iostream>
#include <random>
#include <iomanip> // setprecision(控制输出精度)
using namespace std;

int main() {
    random_device rd;
    mt19937 gen(rd());

    // 场景1:生成[0, 1)的高精度浮点数(OI蒙特卡洛算法常用)
    uniform_real_distribution<double> dist01(0.0, 1.0);
    cout << "[0,1)随机浮点数:" << setprecision(15) << dist01(gen) << endl;

    // 场景2:生成[1.5, 3.5)的浮点数
    uniform_real_distribution<double> dist_ab(1.5, 3.5);
    cout << "[1.5,3.5)随机浮点数:" << setprecision(15) << dist_ab(gen) << endl;

    return 0;
}

【查询】__builtin_popcount

GCC/Clang 自带,无需头文件。

统计二进制中 \(1\) 的个数。

示例代码
#include <iostream>
using namespace std;
using ll = long long; // OI常用64位整型别名

int main() {
    // 测试1:32位正数(用__builtin_popcount)
    unsigned int a = 5; // 二进制101 → 2个1
    cout << "5(101)的1的个数:" << __builtin_popcount(a) << endl; // 输出2

    // 测试2:64位大数(OI高频,必须用__builtin_popcountll)
    unsigned long long b = 1LL << 60; // 二进制仅1个1(第60位)
    cout << "2^60的1的个数:" << __builtin_popcountll(b) << endl; // 输出1

    // 测试3:处理负数(先转无符号数,避免补码问题)
    ll c = -1; // 补码全1,64位下有64个1
    cout << "-1的1的个数:" << __builtin_popcountll((unsigned long long)c) << endl; // 输出64

    // 测试4:混合位(10110 → 3个1)
    unsigned long long d = 22; // 二进制10110
    cout << "22(10110)的1的个数:" << __builtin_popcountll(d) << endl; // 输出3

    return 0;
}

【查询】__builtin_ctz

统计尾随零个数(最低位到第一个 \(1\) 的位数);若输入 \(0\),行为未定义(OI 需先判 \(0\))。

示例代码
#include <iostream>
using namespace std;
using ll = long long;

int main() {
    unsigned long long a = 24; // 二进制11000 → 尾随3个0
    cout << "24的尾随零个数:" << __builtin_ctzll(a) << endl; // 输出3

    // 找n中2的因子个数(如24=2^3×3 → 3个)
    ll n = 24;
    int cnt2 = __builtin_ctzll((unsigned long long)n);
    cout << "24中2的因子个数:" << cnt2 << endl; // 输出3
    return 0;
}

【查询】__builtin_clz

统计前导零个数(最高位到第一个 \(1\) 的位数);输入 \(0\) 行为未定义。

示例代码
#include <iostream>
using namespace std;
using ll = long long;

// 求n的二进制位数(OI高频)
int bit_length(unsigned long long n) {
    if (n == 0) return 0;
    return 64 - __builtin_clzll(n);
}

int main() {
    unsigned long long a = 5; // 二进制101 → 前导61个零(64位)
    cout << "5的前导零个数:" << __builtin_clzll(a) << endl; // 输出61

    // 求最高位1的位置(从0开始计数)
    ll pos = 63 - __builtin_clzll(a); // 63-61=2 → 第2位是最高位1
    cout << "5的最高位1的位置:" << pos << endl; // 输出2

    // 求二进制位数
    cout << "5的二进制位数:" << bit_length(a) << endl; // 输出3
    return 0;
}

【查询】__builtin_ffs

返回最低位 \(1\) 的位置(从 \(1\) 开始计数);输入 \(0\) 返回 \(0\)(无需判 \(0\),更友好)。

示例代码
#include <iostream>
using namespace std;
using ll = long long;

int main() {
    ll a = 24; // 二进制11000 → 最低位1在第4位(从1开始)
    cout << "24的最低位1的位置:" << __builtin_ffsll(a) << endl; // 输出4

    // 提取最低位1的数值(24 → 8)
    ll low_bit = 1LL << (__builtin_ffsll(a) - 1);
    cout << "24的最低位1的数值:" << low_bit << endl; // 输出8

    // 输入0的情况(返回0,无报错)
    cout << "0的最低位1的位置:" << __builtin_ffsll(0) << endl; // 输出0
    return 0;
}

【查询】__builtin_parity

统计二进制中 \(1\) 的个数的奇偶性(偶数个 \(1\) 返回 \(0\),奇数个返回 \(1\))。

示例代码
#include <iostream>
using namespace std;
using ll = long long;

int main() {
    unsigned long long a = 5; // 101 → 2个1(偶数)→ 返回0
    cout << "5的1的个数奇偶性:" << __builtin_parityll(a) << endl; // 输出0

    unsigned long long b = 6; // 110 → 2个1(偶数)→ 返回0
    cout << "6的1的个数奇偶性:" << __builtin_parityll(b) << endl; // 输出0

    unsigned long long c = 7; // 111 → 3个1(奇数)→ 返回1
    cout << "7的1的个数奇偶性:" << __builtin_parityll(c) << endl; // 输出1
    return 0;
}

【查询】__builtin_bitreverse

反转 \(32\) 位二进制位(如 \(0001\)→\(1000\))。其实就是变成字符串后 reverse。

示例代码
#include <iostream>
using namespace std;

int main() {
    unsigned int a = 0b00000001; // 1 → 反转32位后为0x80000000
    cout << "反转1的32位二进制:" << __builtin_bitreverse(a) << endl; // 输出2147483648
    return 0;
}

【构造】next_permutation

基于字典序生成排列。将序列修改为字典序更大的下一个排列,若已是最大排列则返回 \(false\)。生成所有排列(从升序开始)。

warning:效率边界:仅适合 \(n≤10\) 的场景,\(n\) 更大时需剪枝。

示例代码,初始数组必须升序
#include <iostream>
#include <vector>
#include <algorithm> // 必须包含该头文件!
using namespace std;

int main() {
    // 初始序列必须是升序(否则会漏排列)
    vector<int> a = {1, 2, 3};

    cout << "所有全排列(字典序):" << endl;
    do {
        // 输出当前排列
        for (int x : a) cout << x << " ";
        cout << endl;
    } while (next_permutation(a.begin(), a.end())); // 直到无下一个排列

    return 0;
}

【构造】prev_permutation

同 next_permutation 原理,但是是反向枚举排列(从降序开始)。

warning:效率边界:仅适合 \(n≤10\) 的场景,\(n\) 更大时需剪枝。

示例代码,初始数组必须降序
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> a = {3, 2, 1}; // 初始降序

    cout << "反向枚举排列(从降序开始):" << endl;
    do {
        for (int x : a) cout << x << " ";
        cout << endl;
    } while (prev_permutation(a.begin(), a.end()));

    return 0;
}

点个赞吧

posted @ 2026-03-07 10:33  阿蕾奇诺Arlecchino  阅读(28)  评论(0)    收藏  举报