语法和库函数

基础语法

变量

数组初始化

int array[114514] = {0} // 全部初始化为 0
int a[4][5] = {0};

要初始化为其他值,还是需要循环赋值。

static 关键字

修饰全局变量:作用域在文件内

修饰局部变量:只初始化一次

static char[36] flag = "flag{you_cannot_read_me_from_outside}";
int timer1(){
    static int t = 0;
    return ++t;
}

int timer2(){
    static int t;
    t = 0;
    return ++t;
}

int main(){
    for(int i = 0; i < 10; i++){
        printf("%d ", timer1());    // 1 2 3 4 5 6 7 8 9 10
    }
    putchar('\n');
    for(int i = 0; i < 10; i++){
        printf("%d ", timer2());    // 1 1 1 1 1 1 1 1 1 1
    }
    return 0;
}

extern 关键字

来自其他文件的东西,多文件编程

IO

其他库函数

string.h

数据结构(EASY)

链表

其实挺容易犯错的,尤其是在没有想清楚细节的情况下.

最好先想好要修改哪几个节点的数据,修改该节点的前驱还是后继.

合并 K 个有序链表

LeetCode - 合并 K 个有序链表.

这个问题考察了链表的基础使用:

  1. NULL 的判断
  2. 正确更新 node->next
  3. (也许重要)内存管理

可以用到分治法.

node* mergeK(node **lists, int size){
    if(size == 0){
        return NULL;
    }
    else if(size == 1){
        return lists[0];
    }
    int len = size/2;
    return merge2(mergeK(lists, len), mergeK(lists+len, size-len));
}

基于 merge2

node* merge2(node* l1, node* l2) {
    if (l1 == NULL) {
        return l2;
    }
    if (l2 == NULL) {
        return l1;
    }
    node *ans = (node*)malloc(sizeof(node)), *ansp = ans;
    node *p1 = l1, *p2 = l2;
    while (p1 != NULL && p2 != NULL) {
        if (p1->val <= p2->val) {
            ansp->next = p1;
            p1 = p1->next;
        } else {
            ansp->next = p2;
            p2 = p2->next;
        }
        ansp = ansp->next;
    }
    if (p1 != NULL) {
        ansp->next = p1;
    } else if (p2 != NULL) {
        ansp->next = p2;
    }
    ansp = ans->next;
    free(ans);
    return ansp;
}

判环:Floyd's Cycle Finding Algorithm (快慢指针)

用数组模拟链表

next[pos]pos->next 快.

#include <stdio.h>
#include <stdlib.h>
int n, m;
int opt;
int *pre, *next;

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    scanf("%d%d", &n, &m);
    pre = (int *)malloc(sizeof(int) * (n + 1));
    next = (int *)malloc(sizeof(int) * (n + 1));
    for (int i = 1; i <= n; i++)
    {
        pre[i] = i - 1;
        next[i] = i + 1;
    }

    pre[0] = -2;
    next[0] = 1;
    next[n] = -1;
    while (m--)
    {
        int x, y;
        scanf("%d%d%d", &opt, &x, &y);
        switch (opt)
        {
        case 1:
            if (next[x] == y)
            {
                break;
            }
            next[pre[x]] = next[x];
            pre[next[x]] = pre[x];
            next[pre[y]] = x;
            pre[x] = pre[y];
            pre[y] = x;
            next[x] = y;
            break;
        case 2:
            if (next[y] == x)
            {
                break;
            }
            next[pre[x]] = next[x];
            pre[next[x]] = pre[x];
            pre[next[y]] = x;
            next[x] = next[y];
            pre[x] = y;
            next[y] = x;
            break;
        case 3:
            int px = pre[x], nx = next[x];
            int py = pre[y], ny = next[y];
            if (next[x] == y)
            { // x 紧邻 y
                pre[y] = px;
                next[x] = ny;
                next[px] = y;
                pre[ny] = x;
                next[y] = x;
                pre[x] = y;
            }
            else if (next[y] == x)
            { // y 紧邻 x
                pre[x] = py;
                next[y] = nx;
                next[py] = x;
                pre[nx] = y;
                next[x] = y;
                pre[y] = x;
            }
            else
            { // x 和 y 不相邻
                next[px] = y;
                pre[nx] = y;
                next[py] = x;
                pre[ny] = x;

                swap(pre+x,pre+y);
                swap(next+x, next+y);
            }
            break;
        default:
            break;
        }
    }
    int val = 0, cnt = 0;
    unsigned long long sum = 0;
    while (1)
    {
        cnt++;
        val = next[val];
        if (val == -1)
            break;
        if (cnt % 2 == 1)
        {
            sum += val;
        }
    }
    printf("%llu", sum);
    return 0;
}

简易哈希表

基础算法

数学

快速幂

原理是二进制表示: $$ 3^{11} = 3^{(1011)_2} = 3^{(1000)_2} \times 3^{(10)_2} \times 3^{(1)_2} = 3^8 \times 3^2 \times 3^1 $$

long long pow(long long base, int exp){
    long long result = 1;
    while(exp){
        if(exp % 2){		// 这一位是否是 1
            result *= base;
        }
        base *= base;		// 即使不是,也要累乘
        exp /= 2;			// 幂舍去一位
    }
    return result;
}

行列式计算

Laplace 变换

$$ M = \sum (-1)^{i+j}a_{(i,j)}M_{i,j} $$

2阶方阵可以直接求,三阶以上就应该使用 Laplace 降阶。

int laplace(vector<vector<int>> &matrix, int size){
    if(size == 1){
        return matrix[0][0];
    }
    else if(size == 2){
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
    }
    int sum = 0;
    for(int i = 0; i < size; i++){
        vector<vector<int>> submatrix(size-1, vector<int>(size-1));
        for(int j = 1; j < size; j++){
            for(int k = 0; k < size; k++){
                if(k < i){
                    submatrix[j-1][k] = matrix[j][k];
                }
                else if(k > i){
                    submatrix[j-1][k-1] = matrix[j][k];
                }
            }
        }
        sum += matrix[0][i] * pow(-1, i+2) * laplace(submatrix, size-1);
    }
    return sum;
}

LU 分解(应该不至于)

BigNum

结合了之前看到的快读快取的代码

加法

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#define MAX_SIZE 100

char aa[MAX_SIZE], bb[MAX_SIZE], ans[MAX_SIZE + 1];
int head_a, head_b;

int qread(char c[])
{
    int ptr = MAX_SIZE - 1;
    char ch;
    while ((ch = getchar()) && isdigit(ch))
    {
        c[ptr--] = ch;
    }
    return ptr;
}

int qprint(char c[])
{
    int ptr = MAX_SIZE - 1;
    while (c[ptr] != 0)
    {
        putchar(c[ptr--]);
    }
    return ptr;
}

void plus(char a[], char b[])
{
    int offset = head_a - head_b;
    for (int i = head_b + 1; i < MAX_SIZE; i++)
    {
        b[i + offset] = b[i];
    }
    for (int i = MAX_SIZE + offset; i < MAX_SIZE; i++)
    {
        b[i] = '0';
    }
    int dig_l = 0;
    for (int i = MAX_SIZE - 1; i > head_a; i--)
    {
        int dig_a = a[i] - '0';
        int dig_b = b[i] - '0';
        div_t dt = div(dig_a + dig_b + dig_l, 10); // 一个汇编指令,同时得到商和余数
        dig_l = dt.quot;
        ans[i] = dt.rem + '0';
    }
    if (dig_l != 0)
    {
        ans[head_a] = dig_l;
    }
}

int main()
{
    head_a = qread(aa);
    head_b = qread(bb);
    if(head_b >= head_a){
        plus(aa,bb);
    }
    else{
        int temp = head_a;
        head_a = head_b;
        head_b = temp;
        plus(bb,aa);
    }
    qprint(ans);
    return 0;
}

乘除法:

最大公约数

引理:$gcd(a, b) = gcd(b ,a \mod b)$

$O(n)$ 复杂度,Greatest Common Divisor 的欧几里德求法:

// ver1
int gcd(int a, int b){
	return b == 0 ? a : gcd(b, a%b);
}
// ver 2
int gcd(int a, int b){
	while(b != 0){
		int temp = a;
		a = b;
		b = temp % b;
	}
	return a;
}

退出机制是:$gcd(a,0)=|a|$.

最小公倍数

引理:$gcd(a,b) \cdot lcm(a,b) = |a \cdot b|$

lcm = a*b/gcd(a,b);

模拟

螺旋矩阵