粘链接:
[入门]https://www.cnblogs.com/ljh2000-jump/p/5869991.html
[题]https://www.cnblogs.com/vb4896/p/6149022.html
性质:
1:线性基的任意一个非空子集 x o r xor xor起来都不是0。证明可以考虑一定存在一个最高位,使得有且仅有一个。
2:一些数能够 x o r xor xor出来0,当且仅当它们的线性基大小小于原来的集合大小。考虑线性基的本质实际上就是对彼此能异或出来的那些数合并了。如果合并不了,说明原来彼此不可替代,也就不会 x o r xor xor出来0啦。
3:线性基的彼此 x o r xor xor仍然是一组基。
题:
1:板子题,查询一些数的子集的xor和的最大值。
- meet-in-the-middle。插到trie里面。 O ( 2 n 2 ∗ w ) O(2^{\frac{n}{2}}*w) O(22n∗w)
- 线性基板子 O ( n l o g n ) O(nlogn) O(nlogn)
2:查询一个数是否可以子集被 x o r xor xor出来
- 从高位向低位考虑。如果高位是1,一定要选,否则没法表示。这个时候后面位的状态都要 x o r xor xor当前选的这个数的。一旦当前的考虑的数为0了,就代表表示成功。结束
3:hdu3949经典问题,问数的子集中
x
o
r
xor
xor出来的数第
k
k
k小。
对求出来的一组线性基,模仿高斯-约旦消元法。对于每一对
a
i
a_i
ai和
a
j
(
i
>
j
)
a_j(i>j)
aj(i>j),
a
i
=
a
i
x
o
r
a
j
a_i =a_i xor a_j
ai=aixoraj。这就保证了对于每一位,只有1个数唯一存在。对k二进制拆分,可以知道哪些位置必须存在。 注意要对bas重新填位。因为是第k小。
4:bzoj2460
按照权值从大到小贪心。维护当前的线性基来表示当前的id可否被之前的所表示,如果能就GG,否则就加。
5:bzoj2115
经典问题,给一张无向连通图。问1到N的异或值最大的路径。
1:任意一条路径上都可以加一个环而不改变其他的边。 考虑从路径上的一个点出发,遍历这个环再回去。这样出来的这条路径被经过两次就抵消了。只加了这个环。
2: 一些简单环的线性组合一定可以凑出任何一个复杂环。这个就随便画两个环,根据路径抵消发现它们形成了一个更大的环。归纳一下?
做法: 从1开始建立出图的dfs树。记录 d i s i dis_i disi代表1到i的路径上的异或和。对于返祖边,根据 d i s [ u ] x o r d i s [ v ] x o r w [ i ] dis[u] xor dis[v] xor w[i] dis[u]xordis[v]xorw[i] 把这个简单环拿出来。对所有简单环,求一个线性基。以 dis[n]为原状态,从高位到低位不断尝试log个线性基看是否会更大。如果更大就加上。
6:cf724G
给定一张无向图,问有多少个三元组(u,v,z)。满足u到v存在路径的异或和为z,求z的和。
1:每个连通块单独考虑再加起来。
2:拆开位考虑计算贡献。根据上一题,我们知道如果确定了两个点(u,v)的话。u到v的任何一条路径都可以通过(u,v)的一条简单路径xor上某些环得到。
做法1: 枚举两个点(u,v),然后对于每一位。考虑为dis[u]^dis[v],如果这位为1,那么线性基一定要这位不是1。否则一定要这位是1。我们搞出来那种位与位之间互不相同的线性基。就相当于强制一位选or不选,剩下的位瞎选就好了。 O ( n 2 ∗ 64 ∗ l o g n ) O(n^2*64*logn) O(n2∗64∗logn)
做法2:因为对于一个点x,dis[x]是固定不变的。我们考虑只固定一个点x,对于每一位都记录下有a个点的dis位0,有b个点的dis为1。 分四种情况考虑:
- x的这一位为1,线性基里面有1。要么3个都是1,要么线性基那位不选,y的那位选0。
- x的这位为1,线性基里面没有1。那么线性基直接瞎选,y的那位一定选0。
- x的这位为0,线性基里面有1.要么只有线性基选择1,其他两个是0.只有另一个选了1,线性基和x都是0.
- x的这位为0,线性基里面没有1。那么线性基直接瞎选,y的那位一定选1。
这里的x,y是不限制顺序的(抛掉了本身),所有都被重复计算了2遍。最后除以二就好了。
O ( n ∗ 64 ∗ l o g n ) O(n*64*logn) O(n∗64∗logn)可以通过预处理2的幂次来省下里面的一个 l o g log log。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, M = 2e5 + 5, W = 65, P = 1e9 + 7;
int head[N], nxt[M<<1], v[M<<1], vis[N], a[N], tot;
LL w[M<<1], bas[W], pw[W], f[M<<1], dis[N], inv2;
int n, m, siz, cnt, num;
inline LL read() {
LL x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
inline void add(int x, int y, LL z) {
v[++tot] = y;
w[tot] = z;
nxt[tot] = head[x];
head[x] = tot;
}
inline LL q_p(LL a, LL b, LL p) {
LL res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
inline void dfs(int x, int fa) {
vis[x] = 1; a[++siz] = x;
for (int i = head[x]; i; i = nxt[i]) {
int y = v[i];
if (y == fa) continue;
if (!vis[y]) {
dis[y] = dis[x] ^ w[i];
dfs(y, x);
}
else f[++cnt] = dis[x] ^ dis[y] ^ w[i];
}
}
inline void guass() {
num = 0;
for (int i = 1; i <= cnt; ++i) {
for (int j = 63; j >= 0; --j) {
if ((f[i] >> j) & 1) {
if (!bas[j]) {bas[j] = f[i]; break;}
else f[i] ^= bas[j];
}
}
}
//for (int i = 0; i <= 63; ++i) if (bas[i])num++;
for (int i = 63; i >= 0; --i) {
for (int j = i - 1; j >= 0; --j) {
if (bas[i] && bas[j] && ((bas[i] >> j) & 1)) bas[i] ^= bas[j];
}
if (bas[i]) num++;
}
}
inline void upd(LL &a, LL b) {
a += b;
if (a >= P) a -= P;
}
int cnt1[W];
inline LL solve() {
LL res = 0;
memset(cnt1, 0, sizeof cnt1);
for (int i = 1; i <= siz; ++i) {
for (int j = 63; j >= 0; --j) {
if ((dis[a[i]] >> j) & 1) cnt1[j]++;
}
}
int flag;
for (int k = 0; k <= 63; ++k) {
flag = 0;
for (int i = 0; i <= 63; ++i) {
if ((bas[i] >> k) & 1) flag = 1;
}
for (int i = 1; i <= siz; ++i) {
if ((dis[a[i]] >> k) & 1) {
if (flag) upd(res, pw[num - 1] * pw[k] % P * (siz - 1) % P);
else upd(res, pw[num] * pw[k] % P * (siz - cnt1[k]) % P);
}
else {
if (flag) upd(res, pw[num - 1] * pw[k] % P * (siz - 1) % P);
else upd(res, pw[num] * pw[k] % P * cnt1[k] % P);
}
}
}
return res * inv2 % P;
}
int main() {
//freopen("data.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= m; ++i) {
int x = read(), y = read();
LL z = read();
add(x, y, z); add(y, x, z);
}
inv2 = q_p(2, P - 2, P);
pw[0] = 1; LL ans = 0;
for (int i = 1; i < W; ++i) pw[i] = pw[i - 1] * 2 % P;
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
memset(bas, 0, sizeof bas);
cnt = 0; siz = 0;
dfs(i, i);
guass();
upd(ans, solve());
}
}
printf("%I64d\n", ans);
return 0;
}
https://www.cnblogs.com/WR-Eternity/p/10291631.html
注意这里要把每个连通块内的点单独拿出来。如果写一般线性基的话,只需要判断是否存在一位的线性基,它的j位是1就可以了。
7 bzoj4568
线性基合并就暴力合并:
inline void merge(LL *a, LL *b) {// 把b合并到a里
for (int i = 63; i >= 0; --i) {
LL x = b[i];
if (x)
for (int j = 63; j >= 0; --j) {
if ((x >> j) & 1) {
if (!a[j]) {a[j] = x; break;}
else x ^= a[j];
}
}
}
}
1:树剖套线性基合并 O ( n l o g 2 n ∗ 6 4 2 ) O(nlog^2n*64^2) O(nlog2n∗642) 这个东西在Bzoj上面水过去了,在Loj上90pts。
2:树上倍增+线性基合并。 O ( n l o g n ∗ 6 4 2 ) O(nlogn*64^2) O(nlogn∗642) 对于每个点预处理它向上 2 j 2^j 2j步的线性基。预处理和运算的时候同样套一个线性基合并。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 2e4 + 5, W = 66, K = 16;
LL a[N], bas[N][K][W], cur[N];
int f[N][K], dep[N];
int head[N], nxt[N<<1], v[N<<1], tot;
inline void add(int x, int y) {
v[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
inline LL read() {
LL x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, q;
inline void merge(LL *a, LL *b) {
for (int i = 0; i <= 63; ++i) {
LL x = b[i];
if (x)
for (int j = 63; j >= 0; --j) {
if ((x >> j) & 1) {
if (!a[j]) {a[j] = x; break;}
else x ^= a[j];
}
}
}
}
inline void dfs(int x, int fa) {
for (int i = 1; i <= 15; ++i) {
if (dep[x] < (1 << i)) break;
f[x][i] = f[f[x][i - 1]][i - 1];
merge(bas[x][i], bas[x][i - 1]);
merge(bas[x][i], bas[f[x][i - 1]][i - 1]);
}
for (int i = head[x]; i; i = nxt[i]) {
int y = v[i];
if (y == fa) continue;
dep[y] = dep[x] + 1;
f[y][0] = x;
for (int j = 63; j >= 0; --j) {
if ((a[y] >> j) & 1) {
if (!bas[y][0][j]) {bas[y][0][j] = a[y]; break;}
else a[y] ^= bas[y][0][j];
}
}
dfs(y, x);
}
}
inline void debug(LL *a) {
printf("\n");
for (int i = 0; i <= 6; ++i) printf("%lld ", a[i]);
printf("\n\n");
}
inline int LCA(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
int t = dep[x] - dep[y];
for (int i = 0; i <= 15; ++i) {
if ((t >> i) & 1) {
merge(cur, bas[x][i]);
x = f[x][i];
}
}
for (int i = 15; i >= 0; --i) {
if (f[x][i] != f[y][i]) {
merge(cur, bas[x][i]);
merge(cur, bas[y][i]);
x = f[x][i]; y = f[y][i];
}
}
if (x != y) {
merge(cur, bas[x][0]);
merge(cur, bas[y][0]);
merge(cur, bas[f[x][0]][0]);
return f[x][0];
}
else {
merge(cur, bas[x][0]);
return x;
}
}
int main() {
//freopen("loj2013.in", "r", stdin);
//freopen("loj2013.out", "w", stdout);
n = read(); q = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i < n; ++i) {
int x = read(), y = read();
add(x, y); add(y, x);
}
for (int i = 63; i >= 0; --i) {
if ((a[1] >> i) & 1) {
if (!bas[1][0][i]) {bas[1][0][i] = a[1]; break;}
else a[1] ^= bas[1][0][i];
}
}
dfs(1, 1);
for (int i = 1; i <= q; ++i) {
int x = read(), y = read();
memset(cur, 0, sizeof cur);
LCA(x, y);
LL ans = 0;
for (int j = 63; j >= 0; --j) {
if ((ans ^ cur[j]) > ans) ans ^= cur[j];
}
printf("%lld\n", ans);
}
return 0;
}
还是T掉了,可能我常数太大了吧。。