本质还是高斯消元,使其成为上三角矩阵。但是 \(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. 最新MATLAB R2020b超详细安装教程(附完整安装文件)

    摘要:本文详细介绍Matlab R2020b的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用. ...

  2. Exception in thread "main" java.awt.AWTError: Assistive Technology not found: org.GNOME.Accessibilit

    系统环境 Ubuntu 20.04 focal 问题分析 该异常出现的原因,从谷歌上可以得到答案 one of the more common causes of this exception is ...

  3. 登录口爆破之ldap的md5加密、验证码认证

    ldap的md5加密配合autoDecoder插件.captcha-killer-modified插件 autoDecoder例 需要传入的数据包为: {"username":&q ...

  4. ThreadLocal的原理及产生的问题

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. ThreadLocal的原理 特点 ThreadLocal和Sychro ...

  5. umi框架应用服务端SSR,实现数据预渲染

    当我们的应用使用服务端渲染的方式时,可能需要把初始化加载的数据例如推荐等不需要用户输入的内容直接渲染获取,也有利于SEO. 上一篇已经实现服务端渲染,本次实现服务端获取数据后在做渲染. 利用getIn ...

  6. Django学习——路由层之路由匹配、无名分组、有名分组、反向解析

    路由层之路由匹配 """路由你可以看成就是出去ip和port之后的地址""" url()方法 1.第一个参数其实是一个正则表达式 2.一旦第 ...

  7. 五二不休息,今天也学习,从JS执行栈角度图解递归以及二叉树的前、中、后遍历的底层差异

    壹 ❀ 引 想必凡是接触过二叉树算法的同学,在刚上手那会,一定都经历过题目无从下手,甚至连题解都看不懂的痛苦.由于leetcode不方便调试,题目做错了也不知道错在哪里,最后无奈的cv答案后心里还不断 ...

  8. CentOS开机流程详解

    一个执着于技术的公众号 开机流程 BIOS: (Basic Input Output System)基本输入输出系统,它是一组固化到计算机内主板上一个ROM芯片上的程序,保存着计算机最重要的基本输入输 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

  10. MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中

    union 会删除重复数据 union all 不会删除重复数据 select * from ( select *,'a' as kind from tablea where name is not ...