intgauss(){ int rank = 0; // 矩阵的秩 for (int c = 1, r = 1; c <= n; c++) { int t = r; for (int i = r; i <= n; i++) { if (matrix[i].test(c)) { // 找到绝对值最大的行(只要第一个系数是 1 即可) t = i; break; } } if (!matrix[t].test(c)) continue; // 跳过零行 if (t ^ r) swap(matrix[r], matrix[t]); // 交换到第一行
for (int i = 1; i <= n; i++) { // 与普通高斯消元的不同之处,对 1~n 行全部消元 if (matrix[i].test(c) && i ^ r) { // 当前行不消,零行不消 matrix[i] ^= matrix[r]; // 异或代替减法进行消元 } } r++; rank++; } if (rank < n) { // 秩小于 n,可能是无数组解、有可能无解 for (int i = rank + 1; i <= n; i++) { if (matrix[i].test(n + 1)) return NO_SOLUTION; // 存在类似于 0=k 的矛盾情况,无解 } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n + 1; j++) { if (matrix[i].test(j)) { // 无数组解,找到主元并赋值 ans[j] = matrix[i][n + 1]; break; } } } return INFINITE_SOLUTIONS; } for (int i = 1; i <= n; i++) ans[i] = matrix[i][n + 1]; // 有唯一解,对角矩阵每个未知数的解就是常数项的值 return UNIQUE_SOLUTION; }
算法设计:不妨考虑这样几个矩阵 ——
其中一个存储关键词、另一个存储搜索内容。比如我需要将几篇论文(为方便使用英文表示,并且假设词根相同的单词为同一个单词,即不考虑词汇变形)《Basic
Structure of Elemental Monuments》(元素方碑的底层构造)、《Junior
Elemental Reaction》(初等元素反应)、《Advanced Elemental
Theory》(高等元素论)、《Architectural Structure in the Mausoleum of
King Deshret》(赤王陵的建筑结构)以及《Advanced Sumeru Linguistic
Analysis》(高等须弥语音学解析)。我们在此次挑选一些关键词存储(方便读者观察):Structure、Element、Junior
和 Advanced。为每篇论文编号 1-5,矩阵如下图:
返回矩阵的数字代表关键词在对应数据库项中的出现次数,也就是说,如果此时再加入一篇论文《Advanced
Usage of the Word Advanced》(“Advanced”
一词的进阶用法),返回矩阵的最后一行(第六行)会出现一个 。经过虚空终端的一轮排序,它自然就会来到第一位的位置。如果要分别查询两个不同关键词,在查询矩阵右侧再加一列即可,返回矩阵中多出的一列就是新关键词的出现次数;如果需要同时查询两个关键词,在同一列对应位置写入
即可……
voidI() { for (int i = 1; i <= n; i++) mat[i][i] = 1; // 构造2×2单位矩阵 } };
Matrix operator*(const Matrix& l, const Matrix& r) { // 重载乘法运算符,实现矩阵乘法 Matrix res; for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { // res.mat[i][j] = (res.mat[i][j] + l.mat[i][k] * r.mat[k][j] % MOD) % MOD; // 取模用这个 res.mat[i][j] += l.mat[i][k] * r.mat[k][j]; } } } return res; }
Matrix qpow(Matrix a, ll b) { Matrix res; res.I(); // 单位矩阵相当于实数运算中的 1,因此任何矩阵乘以一个单位矩阵的结果都是这个矩阵 while (b) { if (b & 1) res = res * a; a = a * a; b >>= 1; } return res; }
intmain() { int a; cin >> a;
Matrix A; A.mat[1][1] = A.mat[1][2] = 1; // 初始矩阵 Matrix M; M.mat[1][2] = M.mat[2][2] = M.mat[2][1] = 1; // 递推矩阵 A = A * qpow(M, a - 1); cout << A.mat[1][1] << endl; return0; }
XorBase() { flag = false; memset(p, 0, sizeof p); }
boolinsert(ll n) { for (int i = 63; i >= 0; i--) { if ((n >> i) & 1) { if (!p[i]) { p[i] = n; break; } n ^= p[i]; } } if (!n) flag = true; return n; }
voidrebuild() { for (int i = 63; i >= 0; i--) { for (int j = i - 1; j >= 0; j--) { if ((p[i] >> j) & 1) p[i] ^= p[j]; } } }
ll getMin() { if (flag) return0; for (int i = 63; i >= 0; i--) { if (p[i]) return p[i]; } }
ll getMax() { ll ans = 0; for (int i = 63; i >= 0; i--) { ans = max(ans, ans ^ p[i]); } return ans; }
ll MaxK(ll k) { if (k == 1 && flag) return0; if (flag) k--; rebuild(); ll ans = 0; for (int i = 63; i >= 0; i--) { if (p[i]) { if (k & 1) ans ^= p[i]; k >>= 1; } } return ans; } };
四. 并集
思路就是枚举异或线性基
的内容,将其中元素全部加入到
中。
1 2 3 4 5 6 7 8 9 10
XorBase Union(XorBase A, XorBase B) { XorBase res = B; for (int i = 0; i <= 63; i++) { if (A.p[i]) { res.insert(A.p[i]); } } return res; }
五. 交集
如果一个异或线性基里的元素插入到另一个异或线性基里会失败,则将它插入到交集异或线性基中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
XorBase Intersect(XorBase A, XorBase B) { XorBase res; for (int i = 0; i <= 63; i++) { if (A.p[i]) { if (!B.insert(A.p[i])) res.insert(A.p[i]); } if (B.p[i]) { if (!A.insert(B.p[i])) res.insert(B.p[i]); } } return res; }
例 4.3.1 DMG Bonus 打核爆
作为一名资深神原玩家,你希望能在新限定五星角色初进卡池时尽快拿下全网首个
999w
核爆记录,以此证明自己的实力。现在你已经在游戏里做好了很多伤害加成型的食物,准备在
boss
战时一展身手。战斗开始时,你首先给角色吃下了基础食物(每局开始前必吃的食物),它的效果是在
300 秒内单角色爆发伤害增加 ,但是同时它有一个副作用……
XorBase() { flag = false; memset(p, 0, sizeof p); }
boolinsert(ll n) { for (int i = 63; i >= 0; i--) { if ((n >> i) & 1) { if (!p[i]) { p[i] = n; break; } n ^= p[i]; } } if (!n) flag = true; return n; }
voidrebuild() { for (int i = 63; i >= 0; i--) { for (int j = i - 1; j >= 0; j--) { if ((p[i] >> j) & 1) p[i] ^= p[j]; } } }
ll getMin() { if (flag) return0; for (int i = 63; i >= 0; i--) { if (p[i]) return p[i]; } }
ll getMax() { ll ans = 0; for (int i = 63; i >= 0; i--) { ans = max(ans, ans ^ p[i]); } return ans; }
ll MaxK(ll k) { if (k == 1 && flag) return0; if (flag) k--; rebuild(); ll ans = 0; for (int i = 63; i >= 0; i--) { if (p[i]) { if (k & 1) ans ^= p[i]; k >>= 1; } } return ans; } } A;