本质还是高斯消元,使其成为上三角矩阵。但是 \(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. .NET Core(.NET6)中gRPC注册到Consul

    一.简介 上一篇文章介绍了.NET Core 中使用gRPC,在微服务中,我们通常要把服务做成服务注册,服务发现的方式,那么这里来说一下gRPC是如何注册到Consul中的. Consul的安装这里就 ...

  2. 【GPLT】 2018年天梯赛全国总决赛 L2-2 小字辈(c++)

    题目: 这一题并不是很难,属于常规的图论遍历题,这里我是用的bfs(dfs应该也可以,但明显bfs简单一些). 本人写的时候写了很多没必要头文件,自己可以根据内容删去,必要的我会写上注释 如有错误,请 ...

  3. CoAP调试工具 Mozi.IoT.CoAP

    前言 CoAP是一种类HTTP协议的物联网专用协议,其数据包为人类不可阅读的字节流形式,在开发相关应用的时候往往不能准确的了解数据包的内容.故需要专用的调试工具对数据和通讯进行调试. CoAP协议介绍 ...

  4. PDCA循环——快速提升软件质量的必备工具

    近年来,软件项目的规模及其复杂性正在以空前的速度增长,互联网用户市场庞大,互联网公司和相应的软件产品层出不穷.快速响应需求变化往往是互联网行业的常态,软件产品的快速开发迭代对于公司迅速占领市场.抢占商 ...

  5. cnvd进阶学习

    说明 cnvd相对在src漏洞平台中还是比较具备含金量的.今天证书的申请标准就不说了,总归网上都有,主要是想分享下怎么去挖漏洞. 咱们这里只讲通用型漏洞,事件型的暂时我也没挖到.挖通用型漏洞主要方法就 ...

  6. 『现学现忘』Git基础 — 18、Git对象的总结

    目录 1.Git操作最基本的流程 2.工作目录中文件的状态 3.Git效率说明 提示:前面三篇文章已经分别的对blob对象.tree对象.commit对象进行了详细的说明,这篇文章我们总结一下,Git ...

  7. vmware安装或卸载时,显示无法打开注册表项

    ​ vmware卸载是出了名的臭名昭著,因为太难删干净了,删不干净又会有各种各样的问题.比如下文这个"无法打开注册表项" 这个我相信有很多人在重装vmware的时候遇到过,因此我来 ...

  8. ELF文件结构

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

  9. [java并发编程]基于信号量semaphore实现限流器

    目录 一.什么是信号量 二.信号量类Semaphore 三.实现限流器 欢迎关注我的博客,更多精品知识合集 一.什么是信号量 "信号量"在编程术语中使用单词semaphore,那什 ...

  10. VS.NET启动显示ID为XXXX的进程当前未运行

    解决办法:在启动项目根目录下用文本编辑器打开Web项目下的{X}.csproj文件,然后查找 <WebProjectProperties>,将这一对标签之间的内容全部删除,然后再打开项目就 ...