关于 k 进制线性基
本质还是高斯消元,使其成为上三角矩阵。但是 \(k\) 不一定是质数。
但我们不需要保证已有数字不改变,只要维护的是一个上三角矩阵就行。所以我们可以利用更相减损让其中一个向量的最高位 \(= 0\) 。然后插入即可。正确性的证明同二进制线性基。
然后来到了查询环节。在二进制下,异或两次就等于没异或,所以容易判断。但是在这里,一个数的异或次数是不固定的。更糟的是,很有可能有多种方式取得最大值。
举个例子。当前 \(k = 8\) ,最高位 \(= 6\) ,初始数字 \(x = 0\) 。显然异或一次得到 \(6\) 是最好的。但是,异或五次同样可以得到 \(6\) ,而且对后面有影响!
这里就要用一个类似完全背包的操作了。因为,我们确信,如果最高位是 \(v(v\ne 0)\),那么异或这个数 \(\frac{k}{\gcd(k,v)}\) 次,在当前位的异或值不会变。
那么,只要在线性基里额外插入一个当前位置的数的 \(\frac{k}{\gcd(k,v)}\) 次异或值,就会自动完成对后面位置的修正。
最后算一下时间复杂度:
首先计算插入,不妨设 \(k\le x\),否则整个算法是 \(\mathcal O(q+\log x)\) 的。辗转相除最多 \(\mathcal O(\log_2 k)\) 次,每次要 \(\mathcal O(\log_kx)\) 把一整行的信息全部修改,并且每个数字必然从头到尾访问所有 \(\mathcal O(\log_k x)\) 个元素(因为有了上面的 “修正法案”,插入成功也会继续往下尝试插入),复杂度 \(\mathcal O(\log_2 x\cdot \log_k x)\) 。
然后计算查询,仍然设 \(k\le x\),否则整个算法是 \(\mathcal O(q\log x)\) 的。对于每一位,我们要计算当前位可以得到的最小值,这需要 \(\mathcal O(\log_k x)\) 的行向量操作。一共 \(\mathcal O(\log_k x)\) 行,总复杂度 \(\mathcal O(\log_k^2x)\) 。
「IOI2021国家队选拔」术数树
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int q, k, m;
struct Vector {
int a[31];
Vector(int _x = 0) {
memset(a, 0, sizeof(a));
for (int i = 0; _x; i++) {
a[i] = _x % k;
_x /= k;
}
}
inline int &operator[](int t) { return a[t]; }
inline Vector operator+(Vector b) {
Vector res;
for (int i = 0; i < m; i++) res[i] = (a[i] + b[i]) % k;
return res;
}
inline Vector operator-(Vector b) {
Vector res;
for (int i = 0; i < m; i++) res[i] = (a[i] - b[i] + k) % k;
return res;
}
inline Vector operator*(int b) {
Vector res;
for (int i = 0; i < m; i++) res[i] = 1ll * b * a[i] % k;
return res;
}
} bas[31], dis[200005];
int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
void exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
inline int Inv(int x) {
int a, b;
exgcd(x, k, a, b);
return (a + k) % k;
}
inline void insert(Vector x) {
for (int i = m - 1; ~i; i--) {
if (!x[i])
continue;
if (!bas[i][i]) {
bas[i] = x * Inv(x[i]);
x = x * (k / gcd(k, x[i]));
}
while (x[i]) {
int t = bas[i][i] / x[i];
bas[i] = bas[i] - x * t;
swap(x, bas[i]);
}
}
}
inline Vector query(Vector x) {
for (int i = m - 1; ~i; i--) {
if (!bas[i][i])
continue;
x = x - bas[i] * (x[i] / bas[i][i]);
}
return x;
}
int fa[19][200005], dep[200005];
inline int lca(int x, int y) {
if (dep[x] < dep[y])
swap(x, y);
for (int i = 18; ~i; i--)
if (dep[fa[i][x]] >= dep[y])
x = fa[i][x];
if (x == y)
return x;
for (int i = 18; ~i; i--) {
if (fa[i][x] != fa[i][y])
x = fa[i][x], y = fa[i][y];
}
return fa[0][x];
}
int tot = 1;
inline Vector dist(int x, int y) {
int lc = lca(x, y);
return dis[x] + dis[y] - dis[lc] * 2;
}
int main() {
freopen("city.in", "r", stdin);
freopen("city.out", "w", stdout);
scanf("%d%d%d", &q, &k, &m);
for (int i = 0; i < 19; i++) fa[i][1] = 1;
while (q--) {
int op, x, y, v;
scanf("%d", &op);
if (op == 1) {
scanf("%d%d", &x, &v);
insert(Vector(v) + Vector(v));
dis[++tot] = dis[x] + Vector(v);
dep[tot] = dep[x] + 1;
fa[0][tot] = x;
for (int i = 1; i < 19; i++) fa[i][tot] = fa[i - 1][fa[i - 1][tot]];
} else if (op == 2) {
int x, y, v;
scanf("%d%d%d", &x, &y, &v);
insert(dist(x, y) + Vector(v));
} else {
scanf("%d%d", &x, &y);
Vector tmp = query(dist(x, y));
long long res = 0;
for (int i = m - 1; ~i; i--) res = k * res + tmp[i];
printf("%lld\n", res);
}
}
return 0;
}
关于 k 进制线性基的更多相关文章
- CF459C Pashmak and Buses (构造d位k进制数
C - Pashmak and Buses Codeforces Round #261 (Div. 2) C. Pashmak and Buses time limit per test 1 seco ...
- P1066 2^k进制数
传送门 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位. (3)将r转换为2进 ...
- 洛谷 P1066 2^k进制数
P1066 2^k进制数 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位. ( ...
- 快速沃尔什变换(FWT)及K进制异或卷积&快速子集变换(FST)讲解
前言: $FWT$是用来处理位运算(异或.与.或)卷积的一种变换.位运算卷积是什么?形如$f[i]=\sum\limits_{j\oplus k==i}^{ }g[j]*h[k]$的卷积形式(其中$\ ...
- 洛谷P1066 2^k进制数(题解)(递推版)
https://www.luogu.org/problemnew/show/P1066(题目传送) (题解)https://www.luogu.org/problemnew/solution/P106 ...
- K进制数
题目描述 考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0. 考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0. 例: 1010 ...
- 【洛谷p1066】2^k进制数
(不会敲键盘惹qwq) 2^k进制数[传送门] 算法标签: (又是一个提高+省选-的题) 如果我说我没听懂你信吗 代码qwq: #include<iostream> #include< ...
- Ecust DIV3 k进制 【暴力不断优化】
K进制 Description 给定一个正整数n,请你判断在哪些进制下n的表示恰好有2位是1,其余位都是0. Input 输入第一行为整数TT,表示有TT组数据(1 \le T \le 50)(1≤T ...
- 一本通1649【例 2】2^k 进制数
1649:[例 2]2^k 进制数 时间限制: 1000 ms 内存限制: 524288 KB [题目描述] 原题来自:NOIP 2006 提高组 设 r 是个 2k 进制数,并满足以 ...
随机推荐
- 2021.11.11 P4052 [JSOI2007]文本生成器(AC自动机+DP)
2021.11.11 P4052 [JSOI2007]文本生成器(AC自动机+DP) https://www.luogu.com.cn/problem/P4052 题意: JSOI 交给队员 ZYX ...
- CentOS8 AnolisOS8 yum安装 No match for argument: htop Error: Unable to find a match: htop
这里写自定义目录标题 CentOS8 AnolisOS8 yum安装失败 安装失败原因 阿里云官方镜像主页 正确的操作步骤 学习交流 My Blog 技术交流 CentOS8 AnolisOS8 yu ...
- Python实现双X轴双Y轴绘图
诈尸人口回归.这一年忙着灌水忙到头都掉了,最近在女朋友的提醒下终于想起来博客的账号密码,正好今天灌水的时候需要画一个双X轴双Y轴的图,研究了两小时终于用Py实现了.找资料的过程中没有发现有系统的文章, ...
- C#常见控件与SQL Sever数据库交互
C#常见控件与SQL Sever数据库交互 下拉框(ComboBox)与数据库绑定 首先,我们采用DataSet作为临时的数据库,这样会比较好 那么,我们先创建两个成员(对象) string sqlc ...
- JVM组成、GC回收机制、算法、JVM常见启动参数、JAVA出现OOM,如何解决、tomcat优化方法
JVM组成.GC回收机制.算法.JVM常见启动参数.JAVA出现OOM,如何解决.tomcat优化方法
- 尤娜故事-迷雾-springboot扮酷小技巧
前情回顾 从前,有一个简单的通道系统叫尤娜-- 尤娜系统的第一次飞行中换引擎的架构垂直拆分改造 四种常用的微服务架构拆分方式 尤娜,我去面试了 正文 我回到日常的尤娜系统建设中,最近事情比较少,总有一 ...
- GO语言学习——切片三 append()方法为切片添加元素、切片的扩容策略、使用copy()函数复制切片、从切片中删除元素
append()方法为切片添加元素 注意:通过var声明的零值切片可以在append()函数直接使用,无需初始化. var s []int s = append(s, 1, 2, 3) append( ...
- ELF文件结构
ELF文件结构 ELF文件的全称是Executable and Linkable Format,直译为"可执行可链接格式",包括目标文件(.o).可执行文件(可以直接运行).静态链 ...
- 如何在 pyqt 中解决启用 DPI 缩放后 QIcon 模糊的问题
问题描述 如今显示器的分辨率越来越高,如果不启用 DPI 缩放,软件的字体和图标在高分屏下就会显得非常小,看得很累人.从 5.6 版本开始,Qt 便能支持 DPI 缩放功能,Qt6 开始这个功能是默认 ...
- Centos7部署Redis集群
Redis简介 Redis(Remote Dictionary Server)是完全开源的.遵守BSD协议的.高性能的Key-Value数据库. Redis与其他Key-Value缓存产品有一下三个特 ...