本质还是高斯消元,使其成为上三角矩阵。但是 \(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 进制线性基的更多相关文章

  1. 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 ...

  2. P1066 2^k进制数

    传送门 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位. (3)将r转换为2进 ...

  3. 洛谷 P1066 2^k进制数

    P1066 2^k进制数 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位. ( ...

  4. 快速沃尔什变换(FWT)及K进制异或卷积&快速子集变换(FST)讲解

    前言: $FWT$是用来处理位运算(异或.与.或)卷积的一种变换.位运算卷积是什么?形如$f[i]=\sum\limits_{j\oplus k==i}^{ }g[j]*h[k]$的卷积形式(其中$\ ...

  5. 洛谷P1066 2^k进制数(题解)(递推版)

    https://www.luogu.org/problemnew/show/P1066(题目传送) (题解)https://www.luogu.org/problemnew/solution/P106 ...

  6. K进制数

    题目描述 考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0. 考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0. 例: 1010 ...

  7. 【洛谷p1066】2^k进制数

    (不会敲键盘惹qwq) 2^k进制数[传送门] 算法标签: (又是一个提高+省选-的题) 如果我说我没听懂你信吗 代码qwq: #include<iostream> #include< ...

  8. Ecust DIV3 k进制 【暴力不断优化】

    K进制 Description 给定一个正整数n,请你判断在哪些进制下n的表示恰好有2位是1,其余位都是0. Input 输入第一行为整数TT,表示有TT组数据(1 \le T \le 50)(1≤T ...

  9. 一本通1649【例 2】2^k 进制数

    1649:[例 2]2^k 进制数 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:NOIP 2006 提高组 设 r 是个 2k 进制数,并满足以 ...

随机推荐

  1. 2021.11.11 P4052 [JSOI2007]文本生成器(AC自动机+DP)

    2021.11.11 P4052 [JSOI2007]文本生成器(AC自动机+DP) https://www.luogu.com.cn/problem/P4052 题意: JSOI 交给队员 ZYX ...

  2. CentOS8 AnolisOS8 yum安装 No match for argument: htop Error: Unable to find a match: htop

    这里写自定义目录标题 CentOS8 AnolisOS8 yum安装失败 安装失败原因 阿里云官方镜像主页 正确的操作步骤 学习交流 My Blog 技术交流 CentOS8 AnolisOS8 yu ...

  3. Python实现双X轴双Y轴绘图

    诈尸人口回归.这一年忙着灌水忙到头都掉了,最近在女朋友的提醒下终于想起来博客的账号密码,正好今天灌水的时候需要画一个双X轴双Y轴的图,研究了两小时终于用Py实现了.找资料的过程中没有发现有系统的文章, ...

  4. C#常见控件与SQL Sever数据库交互

    C#常见控件与SQL Sever数据库交互 下拉框(ComboBox)与数据库绑定 首先,我们采用DataSet作为临时的数据库,这样会比较好 那么,我们先创建两个成员(对象) string sqlc ...

  5. JVM组成、GC回收机制、算法、JVM常见启动参数、JAVA出现OOM,如何解决、tomcat优化方法

    JVM组成.GC回收机制.算法.JVM常见启动参数.JAVA出现OOM,如何解决.tomcat优化方法

  6. 尤娜故事-迷雾-springboot扮酷小技巧

    前情回顾 从前,有一个简单的通道系统叫尤娜-- 尤娜系统的第一次飞行中换引擎的架构垂直拆分改造 四种常用的微服务架构拆分方式 尤娜,我去面试了 正文 我回到日常的尤娜系统建设中,最近事情比较少,总有一 ...

  7. GO语言学习——切片三 append()方法为切片添加元素、切片的扩容策略、使用copy()函数复制切片、从切片中删除元素

    append()方法为切片添加元素 注意:通过var声明的零值切片可以在append()函数直接使用,无需初始化. var s []int s = append(s, 1, 2, 3) append( ...

  8. ELF文件结构

    ELF文件结构 ELF文件的全称是Executable and Linkable Format,直译为"可执行可链接格式",包括目标文件(.o).可执行文件(可以直接运行).静态链 ...

  9. 如何在 pyqt 中解决启用 DPI 缩放后 QIcon 模糊的问题

    问题描述 如今显示器的分辨率越来越高,如果不启用 DPI 缩放,软件的字体和图标在高分屏下就会显得非常小,看得很累人.从 5.6 版本开始,Qt 便能支持 DPI 缩放功能,Qt6 开始这个功能是默认 ...

  10. Centos7部署Redis集群

    Redis简介 Redis(Remote Dictionary Server)是完全开源的.遵守BSD协议的.高性能的Key-Value数据库. Redis与其他Key-Value缓存产品有一下三个特 ...