算法

虽然是 OIer,经验比较足,但是花时间太少,水平只能说一般。不想卷算法了,没什么兴趣,学到能应对面试的水平就差不多了。

论个人能力,比较擅长数据结构,暴力和骗分,不擅长动归图论数论这不是什么都不会了嘛!。纯暴力神教,因此只会打 OI/IOI 赛制,不会打 ACM。

学习方法

我是看书入门,对入门级网络教程没什么评价。但是在有一定基础的条件下,建议看 OI wiki,大家一起贡献的指南,深度肯定是足够的,不过讲得比较抽象,需要一定理解力。

刷题也很重要 (我好像没有资格说出这句话),看下面 OJ。

OJ

用过一些 Online Judge。一般竞赛洛谷,面试力扣。其他牛客,cf 都不用碰,没有太大意义。

简评一下:

有关我对 OJ 的评价请跳转此处。

自从我会用 Github 以来,我刷的题都会存到 Github 上。这里是仓库链接。

竞赛技巧

打了几次蓝桥,几次 NOIP/CSP,算是有点小经验。

基础

数据结构

图论

动归

我一直是动归苦手。

基础经典动归:背包,最长公共子序列,看模版即可。

数论

素数

素数实在是太重要了。每次比赛几乎必定涉及的考点。

区间求素数一般埃氏筛够用了,也很好写。素数判断可以使用 Miller Rabin。

模板

一些常用模板,如果背下来的话可以省时间。

gcd

C++17 后有 std::gcd 和 std::lcm,不过显然在做题时是没有 C++17 用的。

cpp
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }

优先队列

优先队列平常的使用还挺广泛的,具有较高的重要性。特别是对于自定义 struct 的优先队列需要特别注意。

cpp
#include <queue>
struct Point {
  int x, y;
  bool operator<(const Point &rhs) const { return x + y < rhs.x + rhs.y; }
};
auto q = std::priority_queue<Point>{};
q.push({1, 2});
auto p = q.top();
q.pop();
std::cout << p.x << " " << p.y << std::endl;

permutation

如果使用 >=C++11 / Python,可以很简单地使用标准库:

cpp
#include <algorithm>
do
{}
while (std::next_permutation(s.begin(), s.end()));
py
from itertools import permutations
for p in permutations([1, 2, 3]):
    print(p)

但是在其他语言就得手写 permutation 了。

rust
fn permute<T: std::fmt::Debug>(arr: &mut Vec<T>, start: usize) {
    if start == arr.len() - 1 {
        println!("{:?}", arr);
    } else {
        for i in start..arr.len() {
            arr.swap(start, i);
            permute(arr, start + 1);
            arr.swap(start, i); // backtrack
        }
    }
}
fn main() {
    let mut arr = vec![1, 2, 3];
    permute(&mut arr, 0);
}

总之,如果不是竞赛,只是一个在线 OJ 笔试,那肯定是用越方便的语言越好。如果你很熟练,快速糊一个 permutation 也无妨。

背包问题

基础背包:

cpp
for (int i = 0; i < n; ++i) {
    for (int j = capacity; j >= weights[i]; --j) {
        dp[j] = std::max(dp[j], dp[j - weights[i]] + values[i]);
    }
}

完全背包(仅需要修改遍历顺序):

cpp
for (int i = 0; i < n; ++i) {
    for (int j = weights[i]; j <= capacity; ++j) {
        dp[j] = std::max(dp[j], dp[j - weights[i]] + values[i]);
    }
}

而其他背包问题基本都是可拆的。

树状数组

单点加,区间和

cpp
inline ll lowbit(ll x) { return x & (-x); }
void add(ll x, ll k) {
  for (int i = x; i <= n; i += lowbit(i))
    s[i] += k;
}
ll sum(ll x) // [1, x]
{
  ll tmp = 0;
  for (int i = x; i; i -= lowbit(i))
    tmp += s[i];
  return tmp;
}

并查集

cpp
int fa(int i) {
  if (i == father[i])
    return i;
  else
    return father[i] = fa(father[i]);
}

我出的题

大一的时候来了兴致,随便出的一点。当乐子看吧(

download

external

  1. 信息学竞赛 (OI) 究竟发生了什么?
  2. Advent of Code
  3. sort_algorithms:具有教学意义的一个排序实现库,实现了很多有趣的排序算法