程序设计笔记

CXX

常见快速类型转换

1.int ->string

#include <string>
#include <iostream>

int num = 123;
std::string str = std::to_string(num);
std::cout << str << std::endl; // 输出 "123"

2.string -> int

#include <sstream>
int str_to_int(string str_) {
  int num;
  std::stringstream ss(str_);
  ss >> num; // 从字符串流读取到 int
  return num;
  //0123 -> 123
}

递归写法

题目:递归左右区间,单个元素返回值

偶数区间返回L*R,奇数返回|L-R| * mid * |L+R|
使用带返回值的写法即可

lld dfs(const vector<lld> s) {
  //
  if (s.size() == 1)
    return s[0] mod MOD_N;

  lld size_ = s.size() / 2;

  if (s.size() % 2 == 0) {
    vector<lld> s1(s.begin(), s.begin() + size_);
    vector<lld> s2(s.begin() + size_, s.end());
    lld L = dfs(s1);
    lld R = dfs(s2);
    return mod_mul(L, R);

  }

  else {
    vector<lld> s1(s.begin(), s.begin() + size_);
    vector<lld> s2(s.begin() + size_ + 1, s.end());
    lld mid = s[size_];
    lld L = dfs(s1);
    lld R = dfs(s2);
    lld LR = mod_mul(std::abs(mod_add(L, -R)), mod_add(L, R));
    return mod_mul(mid, LR);
  }
}
void solve() {
  int n;
  cin >> n;
  vector<lld> s(n);
  for (auto &i : s)
    cin >> i;
  sum = dfs(s);
  std::cout << sum << ENDL;
}

STL 常见用法

String

快速清空数组为 0/-1,但是不建议为其他除了 0/-1 之外的数字

#include <cstring> // for memset

int arr[1000];
memset(arr, 0, sizeof(arr)); // 全部置 0

1.vector

区间表示法:[begin, end),即左闭右开区间

//从已有vector创建新的vector
bool check(vector<int> a) {
  int n = a.size();
  for (int pos = 1; pos < n; ++pos) {
    vector<lld> left(a.begin(), a.begin() + pos);
    // left创建了从[start,pos)的区间

    vector<lld> right(a.begin() + pos, a.end());
    // right创建了从[pos,end)的区间
  }
}

2.哈希表和红黑树实现的容器

  1. unorder_set
    特点:无序集合,存储唯一的元素,不允许重复

#include <unordered_set>
using std::unordered_set;

int main() {
  unordered_set<int> s1;             // 空集合
  unordered_set<int> s2 = {1, 2, 3}; // 初始化列表
  unordered_set<int> s3(s2);         // 拷贝构造

  // 插入元素
  s1.insert(4);         // 插入单个元素
  s1.insert({5, 6, 7}); // 插入多个元素
  s1.emplace(8);        // 原地构造元素

  // 判断元素是否存在
  if (s1.find(5) != s1.end()) {
    // 元素存在
  }

  // 使用 count 检查存在性(返回0或1)
  if (s1.count(5)) {
    // 元素存在
  }

  // 删除元素
  s1.erase(5);          // 删除值为5的元素
  s1.erase(s1.begin()); // 删除第一个元素(任意)
  s1.clear();           // 清空集合

  // 获取容量

  bool isEmpty = s1.empty();
  size_t size = s1.size();

  // 遍历容器
  for (int num : s1) {
    cout << num << " ";
  }

  for (auto it = s1.begin(); it != s1.end(); ++it) {
    cout << *it << " ";
  }

  // 获取某个元素的位置 使用迭代器
  auto it = s1.find(5);
  if (it != s1.end()) {
    // 可以通过*it访问元素
  }
}

2.unordered_multiset
无序多重集合,允许存储重复元素

#include <iostream>
#include <unordered_set>
using std::unordered_multiset;

int main() {
  // 初始化
  unordered_multiset<int> s1;                // 空集合
  unordered_multiset<int> s2 = {1, 2, 2, 3}; // 初始化列表,允许重复
  unordered_multiset<int> s3(s2);            // 拷贝构造

  // 插入元素
  s1.insert(4);         // 插入单个元素
  s1.insert({5, 5, 6}); // 插入多个元素,允许重复
  s1.emplace(7);        // 原地构造元素

  // 判断元素是否存在
  if (s1.find(5) != s1.end()) {
    std::cout << "元素 5 存在\n";
  }

  // 使用 count 检查存在性及出现次数
  if (s1.count(5)) {
    std::cout << "元素 5 出现 " << s1.count(5) << " 次\n";
  }

  // 删除元素
  s1.erase(5);          // 删除所有值为 5 的元素
  s1.erase(s1.begin()); // 删除任意一个元素
  s1.clear();           // 清空集合

  // 获取容量
  bool isEmpty = s1.empty();
  size_t size = s1.size();

  // 遍历容器
  for (int num : s2) {
    std::cout << num << " ";
  }
  std::cout << "\n";

  for (auto it = s2.begin(); it != s2.end(); ++it) {
    std::cout << *it << " ";
  }
  std::cout << "\n";

  // 获取某个元素的位置 使用迭代器
  auto it = s2.find(2);
  if (it != s2.end()) {
    // 成功找到会输出2
    std::cout << *it << "\n";
  }

  return 0;
}

3.unordered_map

无序键值对映射,键唯一,基于哈希表实现

#include <iostream>
#include <unordered_map>
using std::unordered_map;

int main() {
    // 初始化
    unordered_map<int, std::string> m1;                     // 空映射
    unordered_map<int, std::string> m2 = {{1, "one"}, {2, "two"}}; // 初始化列表
    unordered_map<int, std::string> m3(m2);                 // 拷贝构造

    // 插入元素
    m1.insert({4, "four"});         // 插入单个键值对
    m1.insert({{5, "five"}, {6, "six"}}); // 插入多个键值对
    m1.emplace(7, "seven");        // 原地构造键值对

    // 判断键是否存在
    if (m1.find(5) != m1.end()) {
        std::cout << "键 5 存在\n";
    }

    // 使用 count 检查键存在性(返回 0 或 1)
    if (m1.count(5)) {
        std::cout << "键 5 存在\n";
    }

    // 使用 operator[] 访问或插入
    m1[8] = "eight"; // 如果键 8 不存在,则插入

    // 删除元素
    m1.erase(5);          // 删除键为 5 的键值对
    m1.erase(m1.begin()); // 删除任意一个键值对
    m1.clear();           // 清空映射

    // 获取容量
    bool isEmpty = m1.empty();
    size_t size = m1.size();

    // 遍历容器
    for (const auto& pair : m2) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << "\n";

    for (auto it = m2.begin(); it != m2.end(); ++it) {
        std::cout << it->first << ": " << it->second << " ";
    }
    std::cout << "\n";

    // 获取某个键的位置 使用迭代器
    auto it = m2.find(2);
    if (it != m2.end()) {
        std::cout << "找到键值对: " << it->first << ": " << it->second << "\n";
    }

    return 0;
}

unordered_multimap

无序键值对映射,允许重复键,基于哈希表实现

键可以重复,同一键可对应多个值:

#include <iostream>
#include <unordered_map>
using std::unordered_multimap;

int main() {
    // 初始化
    unordered_multimap<int, std::string> mm1;                     // 空映射
    unordered_multimap<int, std::string> mm2 = {{1, "one"}, {1, "uno"}, {2, "two"}}; // 初始化列表
    unordered_multimap<int, std::string> mm3(mm2);                // 拷贝构造

    // 插入元素
    mm1.insert({4, "four"});         // 插入单个键值对
    mm1.insert({{5, "five"}, {5, "cinco"}}); // 插入多个键值对,允许重复键
    mm1.emplace(6, "six");          // 原地构造键值对

    // 判断键是否存在
    if (mm1.find(5) != mm1.end()) {
        std::cout << "键 5 存在\n";
    }

    // 使用 count 检查键存在性及出现次数
    if (mm1.count(5)) {
        std::cout << "键 5 出现 " << mm1.count(5) << " 次\n";
    }

    // 删除元素
    mm1.erase(5);          // 删除所有键为 5 的键值对
    mm1.erase(mm1.begin()); // 删除任意一个键值对
    mm1.clear();           // 清空映射

    // 获取容量
    bool isEmpty = mm1.empty();
    size_t size = mm1.size();

    // 遍历容器
    for (const auto& pair : mm2) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << "\n";

    for (auto it = mm2.begin(); it != mm2.end(); ++it) {
        std::cout << it->first << ": " << it->second << " ";
    }
    std::cout << "\n";

    // 获取某个键的位置 使用迭代器
    auto it = mm2.find(1);
    if (it != mm2.end()) {
        std::cout << "找到键值对: " << it->first << ": " << it->second << "\n";
    }

    // 获取所有键为 1 的键值对
    auto range = mm2.equal_range(1);
    for (auto i = range.first; i != range.second; ++i) {
        std::cout << i->first << ": " << i->second << " ";
    }
    std::cout << "\n";

    return 0;
}

基于红黑树实现的容器

set:有序集合,存储唯一元素

#include <iostream>
#include <set>
using std::set;

int main() {
    // 初始化
    set<int> s1;             // 空集合
    set<int> s2 = {1, 2, 3}; // 初始化列表
    set<int> s3(s2);         // 拷贝构造

    // 插入元素
    s1.insert(4);         // 插入单个元素
    s1.insert({5, 6, 7}); // 插入多个元素
    s1.emplace(8);        // 原地构造元素

    // 判断元素是否存在
    if (s1.find(5) != s1.end()) {
        std::cout << "元素 5 存在\n";
    }

    // 使用 count 检查存在性(返回 0 或 1)
    if (s1.count(5)) {
        std::cout << "元素 5 存在\n";
    }

    // 删除元素
    s1.erase(5);          // 删除值为 5 的元素
    s1.erase(s1.begin()); // 删除第一个元素(最小值)
    s1.clear();           // 清空集合

    // 获取容量
    bool isEmpty = s1.empty();
    size_t size = s1.size();

    // 遍历容器
    for (int num : s2) {
        std::cout << num << " ";
    }
    std::cout << "\n";

    for (auto it = s2.begin(); it != s2.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";

    // 获取某个元素的位置 使用迭代器
    auto it = s2.find(2);
    if (it != s2.end()) {
        std::cout << "找到元素: " << *it << "\n";
    }

    return 0;
}

multiset:有序多重集合,允许重复元素

#include <iostream>
#include <set>
using std::multiset;

int main() {
    // 初始化
    multiset<int> s1;             // 空集合
    multiset<int> s2 = {1, 2, 2, 3}; // 初始化列表,允许重复
    multiset<int> s3(s2);         // 拷贝构造

    // 插入元素
    s1.insert(4);         // 插入单个元素
    s1.insert({5, 5, 6}); // 插入多个元素,允许重复
    s1.emplace(7);        // 原地构造元素

    // 判断元素是否存在
    if (s1.find(5) != s1.end()) {
        std::cout << "元素 5 存在\n";
    }

    // 使用 count 检查存在性及出现次数
    if (s1.count(5)) {
        std::cout << "元素 5 出现 " << s1.count(5) << " 次\n";
    }

    // 删除元素
    s1.erase(5);          // 删除所有值为 5 的元素
    s1.erase(s1.begin()); // 删除第一个元素(最小值)
    s1.clear();           // 清空集合

    // 获取容量
    bool isEmpty = s1.empty();
    size_t size = s1.size();

    // 遍历容器
    for (int num : s2) {
        std::cout << num << " ";
    }
    std::cout << "\n";

    for (auto it = s2.begin(); it != s2.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";

    // 获取某个元素的位置 使用迭代器
    auto it = s2.find(2);
    if (it != s2.end()) {
        std::cout << "找到元素: " << *it << "\n";
    }

    return 0;
}

map:序键值对映射,键唯一

#include <iostream>
#include <map>
using std::map;

int main() {
    // 初始化
    map<int, std::string> m1;                     // 空映射
    map<int, std::string> m2 = {{1, "one"}, {2, "two"}}; // 初始化列表
    map<int, std::string> m3(m2);                 // 拷贝构造

    // 插入元素
    m1.insert({4, "four"});         // 插入单个键值对
    m1.insert({{5, "five"}, {6, "six"}}); // 插入多个键值对
    m1.emplace(7, "seven");        // 原地构造键值对

    // 判断键是否存在
    if (m1.find(5) != m1.end()) {
        std::cout << "键 5 存在\n";
    }

    // 使用 count 检查键存在性(返回 0 或 1)
    if (m1.count(5)) {
        std::cout << "键 5 存在\n";
    }

    // 使用 operator[] 访问或插入
    m1[8] = "eight"; // 如果键 8 不存在,则插入

    // 删除元素
    m1.erase(5);          // 删除键为 5 的键值对
    m1.erase(m1.begin()); // 删除第一个键值对(最小键)
    m1.clear();           // 清空映射

    // 获取容量
    bool isEmpty = m1.empty();
    size_t size = m1.size();

    // 遍历容器
    for (const auto& pair : m2) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << "\n";

    for (auto it = m2.begin(); it != m2.end(); ++it) {
        std::cout << it->first << ": " << it->second << " ";
    }
    std::cout << "\n";

    // 获取某个键的位置 使用迭代器
    auto it = m2.find(2);
    if (it != m2.end()) {
        std::cout << "找到键值对: " << it->first << ": " << it->second << "\n";
    }

    return 0;
}

multimap:

有序键值对映射,允许重复键,

#include <iostream>
#include <map>
using std::multimap;

int main() {
    // 初始化
    multimap<int, std::string> mm1;                     // 空映射
    multimap<int, std::string> mm2 = {{1, "one"}, {1, "uno"}, {2, "two"}}; // 初始化列表
    multimap<int, std::string> mm3(mm2);                // 拷贝构造

    // 插入元素
    mm1.insert({4, "four"});         // 插入单个键值对
    mm1.insert({{5, "five"}, {5, "cinco"}}); // 插入多个键值对,允许重复键
    mm1.emplace(6, "six");          // 原地构造键值对

    // 判断键是否存在
    if (mm1.find(5) != mm1.end()) {
        std::cout << "键 5 存在\n";
    }

    // 使用 count 检查键存在性及出现次数
    if (mm1.count(5)) {
        std::cout << "键 5 出现 " << mm1.count(5) << " 次\n";
    }

    // 删除元素
    mm1.erase(5);          // 删除所有键为 5 的键值对
    mm1.erase(mm1.begin()); // 删除第一个键值对(最小键)
    mm1.clear();           // 清空映射

    // 获取容量
    bool isEmpty = mm1.empty();
    size_t size = mm1.size();

    // 遍历容器
    for (const auto& pair : mm2) {
        std::cout << pair.first << ": " << pair.second << " ";
    }
    std::cout << "\n";

    for (auto it = mm2.begin(); it != mm2.end(); ++it) {
        std::cout << it->first << ": " << it->second << " ";
    }
    std::cout << "\n";

    // 获取某个键的位置 使用迭代器
    auto it = mm2.find(1);
    if (it != mm2.end()) {
        std::cout << "找到键值对: " << it->first << ": " << it->second << "\n";
    }

    // 获取所有键为 1 的键值对
    auto range = mm2.equal_range(1);
    for (auto i = range.first; i != range.second; ++i) {
        std::cout << i->first << ": " << i->second << " ";
    }
    std::cout << "\n";

    return 0;
}

位运算

使用 01 串枚举所有的可能,根据 01 来决定如何运算

bool f(int n) {
  for (int i = 0; i < 1 << (n - 1); ++i) {
    // 0-2^n-1-1
    //
    for (int j = 1; j < n; ++j) {
      // 检查j的第 j-1 位是否为 1
      // 如果是 则为 true 进入if
      // 否则进入 else
      if (i & 1 << (j - 1)) {
        return false;
        // body
      } else {
        return false;
        // body
      }
    }
  }
  return false;
}

查找类

高效查找两个固定数组的共同元素

哈希

//哈希
#include <unordered_set>
#include <vector>

long long count_ele(const std::vector<long long> &a,
                    const std::vector<long long> &b) {
  std::unordered_set<long long> set_a(a.begin(),
                                      a.end()); // 将数组 a 存入哈希表
  long long count = 0;
  for (int num : b) {
    if (set_a.count(num)) { // 检查 b 的元素是否在 a 中存在
      count++;
    }
  }
  return count;
}

双指针实现

long long count_elements(std::vector<long long> &a, std::vector<long long> &b) {
  std::sort(a.begin(), a.end());
  std::sort(b.begin(), b.end());
  long long i = 0, j = 0, count = 0;
  while (i < a.size() && j < b.size()) {
    if (a[i] == b[j]) {
      count++;
      i++;
      j++;
    } else if (a[i] < b[j]) {
      i++;
    } else {
      j++;
    }
  }
  return count;
}

双指针

排序后记录出现次数大于等于 2 的元素个数

  // 1 1 1 2 2 3  sum=2
  int sum = 0;
  vector<int> s;
  int n = s.size();
  for (int i = 0, j = 1; j < n;) {
    while (j < n and s[i] == s[j])
      j++;
    if (j - i >= 2) {
      sum++;
    }
    i = j;
  }

双指针算矩形面积


数论

gcd lcm

lld gcd(lld x, lld y) {
  if (y == 0) {
    return x;
  } else {
    return gcd(y, x % y);
  }
}

lld lcm(lld x, lld y) {
  return x * y / gcd(x, y);
}

T 个询问区间[L,R]多少个素数

const int MX = 1e5 + 10; // 只需预处理到sqrt(1e9)≈31623
vector<int> p;           // 存储小素数

// 预处理所有小于√R的素数 存储在数组p
void init() {
  // np[i]=true表示i不是素数
  vector<bool> np(MX);
  np[0] = np[1] = true;
  for (int i = 2; i < MX; ++i) {
    if (!np[i])
      p.push_back(i);
    for (int j = 0; j < p.size() && i * p[j] < MX; ++j) {
      np[i * p[j]] = true;
      if (i % p[j] == 0)
        break;
    }
  }
}

// 分段筛法查询区间[L,R]的素数个数
lld qry(lld L, lld R) {
  if (L > R)
    return 0;
  if (R < 2)
    return 0;

  // 标记区间[L,R]内的数是否为素数
  vector<bool> seg(R - L + 1, true);

  for (lld prime : p) {
    if (prime * prime > R)
      break;

    lld start = max(prime * prime, (L + prime - 1) / prime * prime);
    for (lld j = start; j <= R; j += prime) {
      seg[j - L] = false;
    }
  }

  if (L == 1)
    seg[0] = false; // 1不是素数

  lld cnt = 0;
  for (bool is_prime : seg) {
    cnt += is_prime;
  }
  return cnt;
}

int main() {
  init();
  int T = 1;
  cin >> T;
  while (T--) {
    lld L, R;
    cin >> L >> R;
    cout << qry(L, R) << std::endl;
  }
  return 0;
}

附录

ASCII 对照表

//number
0: 48
b: 49
...
9: 57
//快速把char输出为int
cout << x - 48 << endl;


//字母
a: 97
b: 98
...
z: 122

头文件


#include <algorithm>
#include <cstdio>
#include <cstring>
#include <functional> // 需要包含这个头文件以使用 std::greater
#include <iostream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_set>
#include <vector>

#define lld long long // long long 的 printf 占位符是 lld
#define ENDL '\n'     // 将 endl 替换为 \n 取消缓冲区
#define mod %
const lld N = 1e5 + 9;
const lld MOD_N = 1e9 + 7;
const lld MAX_ = 1e9;

using std::cin;
using std::cout;
using std::priority_queue;
using std::queue;
using std::set;
using std::stack;
using std::string;
using std::unordered_set;
using std::vector;

// 大根堆 小根堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
std::priority_queue<int> max_heap;

// 自定义排序
auto cmp = [](int a, int b) {
  if (a % 2 == b % 2)
    return a > b;       // 同奇偶时降序
  return a % 2 < b % 2; // 奇数优先
};
std::priority_queue<int, vector<int>, decltype(cmp)> custom_heap(cmp);

// 按照年龄
struct Person {
  string name;
  int age;
};
auto cmp_ = [](Person a, Person b) {
  return a.age > b.age;
  // 注意是 >,表示小的在前
  // 小根堆年龄
  // 也就是实际是20 25 30
};
priority_queue<Person, vector<Person>, decltype(cmp_)> age_heap(cmp_);

//同理其他的容器 set
std::set<lld, decltype(cmp)> st(cmp);

lld safe_mod(lld x) {
  // 对负数取mod 保证结果在 [0, MOD-1]
  return (x % MOD_N + MOD_N) % MOD_N;
}
// 取模加法 (a + b) % MOD_N
lld mod_add(lld a, lld b) { return (a mod MOD_N + b mod MOD_N) mod MOD_N; }

// 取模乘法 (a * b) % MOD_N
lld mod_mul(lld a, lld b) { return (a mod MOD_N * b mod MOD_N) mod MOD_N; }

// 取模快速幂 (a^b) % MOD_N
lld mod_pow(lld a, lld b) {
  lld res = 1;
  while (b > 0) {
    if (b & 1)
      res = mod_mul(res, a);
    a = mod_mul(a, a);
    b >>= 1;
  }
  return res;
}

// gcd 和 lcm
lld gcd(lld x, lld y) {
  if (y == 0) {
    return x;
  } else {
    return gcd(y, x % y);
  }
}

lld lcm(lld x, lld y) { return x * y / gcd(x, y); }

inline int read();
inline void write(lld x);

bool check(lld mid) {
  // **body**
}
int bin_sh(lld L, lld R) {
  lld mid = L + (R - L) / 2;
  while (L <= R) {
    mid = L + (R - L) / 2;
    if (check(mid)) {
      L = mid + 1; // 调整左边界
    } else {
      R = mid - 1;
    }
  }
  return mid;
}
void solve() {
  // body
}
int main() {
  std::ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  int T = 1;
  cin >> T;
  while (T--) {
    solve();
  }
}
// lld read 和 lld 类型的 write
inline lld read() {
  lld x = 0, f = 1;
  char ch = getchar();
  while (!isdigit(ch)) {
    if (ch == '-')
      f = -1;
    ch = getchar();
  }
  while (isdigit(ch)) {
    x = x * 10 + (ch - '0');
    ch = getchar();
  }
  return x * f;
}
//修改参数类型即可
inline void write(lld x) {
  if (x < 0)
    putchar('-'), x = -x;
  if (x > 9)
    write(x / 10);
  putchar(x % 10 + '0');
}
posted @ 2025-03-30 18:51  phrink  阅读(10)  评论(0)    收藏  举报