数学算法

本文最后更新于:15 天前

筛质数

埃氏筛法(埃拉托斯特尼筛法)

const int N = 1e7;//筛的范围
bool st[N + 10];//判断是否为质数 初始为false false为质数 true为非质数
int save[N + 10], idx = 0;//用来存储质数
st[0] = st[1] = true;
for (int i = 2; i <= N; i ++ ){
	if (!st[i]) save[idx ++ ] = i;
	else continue;//优化循环节省时间
	for (int j = i * 2; j <= N; j += i )
		st[j] = true;
}
idx --;

线性筛法(欧拉筛法)

const int N = 1e7;//筛要的范围
bool st[N + 10];//判断是否为质数 初始为false false为质数 true为非质数
int save[N + 10], idx = 0;//用来存储质数
a[0] = a[1] = true;
for (int i = 2; i <= N; i ++ ){
	if (!st[i]) save[idx ++ ] = i;
	for (int j = 0; save[j] * i <= N; j ++ ){
        st[save[j]*i] = true;
        if (i % save[j] == 0) break;//优化循环节省时间 每个数只删一次
    }

}
idx --;

博弈论详解

一. 巴什博奕(Bash Game)

只有一堆 n 个物品, 两人轮流从这堆物品中取物,规定每次至少取一个,最多取 m 个。最后取光者得胜。

#include<bits/stdc++.h>
#define _MAX 10000
using namespace std;

int a[_MAX], b[_MAX];

int bash(int N, int K){
	if(N % (K + 1) == 0){
		return 2;
	}
	return 1;
}

int main(){
	int T;
	scanf("%d", &T);
	for(int i = 0; i < T; i++){
		scanf("%d%d", a + i, b + i);
	}
	for(int i = 0; i < T; i++){
		if(bash(a[i], b[i]) == 1){
			puts("A");
		}else{
			puts("B");
		}
	}
	return 0;
}

二. 威佐夫博弈(Wythoff Game)

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int t, a, b, m, k;
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &a, &b);
        if(a > b){
            a ^= b;
            b ^= a;
            a ^= b;
        }
        m = b - a;
        k = (int)(m * (1 + sqrt(5)) / 2.0);
        printf("%s\n", a == k ? "B" : "A");
    }
	return 0;
}

三. 尼姆博弈(Nimm Game)

有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者获胜。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int N, stone, tag = 0;
    scanf("%d", &N);
    while(N--){
        scanf("%d", &stone);
        tag ^= stone;
    }
    printf("%c\n", tag == 0 ? 'B' : 'A');
	return 0;
}

四. SG 函数

  • SG 打表
const int MAX_DIG = 64;

int f[MAX_DIG];         // 可以取走的石子个数
int sg[MAX_DIG];        // 0~n的SG函数值
int has[MAX_DIG];       // mex{}

void getSG(int n){
    memset(sg, 0, sizeof(sg));
    for(int i = 1; i <= n; i++){
        memset(has,0,sizeof(has));
        for(int j = 1; f[j] <= i; j++){
            has[sg[i - f[j]]] = 1;
        }
        for(int j = 0; j <= n; j++){        // 求mes{}中未出现的最小的非负整数
            if(has[j] == 0){
                sg[i] = j;
                break;
            }
        }
    }
}
  • SG DFS
const int MAX_DIG = 64;

// DFS
// 注意S数组要按从小到大排序SG函数要初始化为-1 对于每个集合只需初始化1遍
// n是集合s的大小S[i]是定义的特殊取法规则数组
int s[MAX_DIG];
int sg[MAX_DIG * 100];
int n;

int SG_dfs(int x){
    if(sg[x] != -1){
        return sg[x];
    }
    bool vis[MAX_DIG];
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i < n; i++){
        if(x >= s[i]){
            SG_dfs(x - s[i]);
            vis[sg[x - s[i]]] = 1;
        }
    }
    int e;
    for(int i = 0;;i++){
        if(!vis[i]){
            e = i;
            break;
        }
    }
    return sg[x] = e;
}