线性基 题目
BZOJ-2115 Xor
Description
Input
第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。
Output
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。
Sample Input
5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2
Sample Output
6
Hint
题解
我们首先求出一条从1到n简单路径的异或和,我们考虑如何让走过的路径异或和最大。
对于一个环的异或和,显然是可以被加入的(题目保证联通),因为我们可以沿一条路径进入环绕一圈后再出来,所以我们统计所有环的异或和,把它们加入线性基,最后要求的答案就是异或和最大的简单路径再与这些环的异或和求一个异或最大值。
至于异或和最大的简单路径,我们先随便求一条即可,如果存在异或和更大的从1到n简单路径,那么这两条路径一定形成一个环,在异或的过程中就会替换掉不是最长的1到n的路径
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
ll b[65], p[65];
int cnt, flag;
node() {
memset(p, 0, sizeof(p));
memset(b, 0, sizeof(b));
cnt = flag = 0;
}
bool insert(ll x) {
for (int i = 62; i >= 0; i--) {
if (x & (1LL << i)) {
if (b[i]) {
x ^= b[i];
}
else {
b[i] = x;
return true;
}
}
}
flag = 1;
return false;
}
ll qmax(ll base) {
ll res = base;
for (int i = 62; i >= 0; i--) {
if ((res ^ b[i]) > res) res ^= b[i];
}
return res;
}
ll qmin() {
if (flag) return 0;
for (int i = 0; i <= 62; i++) {
if (b[i]) return b[i];
}
return 0;
}
void rebuild() {
for (int i = 62; i >= 1; i--) {
if (b[i]) {
for (int j = i - 1; j >= 0; j--) {
if (b[i] & (1LL << j)) b[i] ^= b[j];
}
}
}
for (int i = 0; i <= 62; i++) {
if (b[i]) p[cnt++] = b[i];
}
}
ll kth(ll k) {
if (flag) --k;
if (k == 0) return 0;
ll res = 0;
if (k >= (1LL << cnt)) return -1;
for (int i = 0; i < cnt; i++) {
if (k & (1LL << i)) res ^= p[i];
}
return res;
}
} lis;
node merge(node n1, node n2) {
node res = n1;
for (int i = 0; i <= 62; i++) {
if (n2.b[i]) res.insert(n2.b[i]);
}
res.flag = n1.flag | n2.flag;
return res;
}
struct edge {
int v; ll w;
edge(int v = 0, ll w = 0): v(v), w(w) {}
};
const int N = 5e4 + 10;
vector<edge> G[N];
ll a[N];
bool vis[N];
void dfs(int u) {
vis[u] = 1;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].v;
if (vis[v]) lis.insert(a[u] ^ a[v] ^ G[u][i].w);
else {
a[v] = a[u] ^ G[u][i].w;
dfs(v);
}
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v; ll w;
scanf("%d%d%lld", &u, &v, &w);
G[u].push_back(edge(v, w));
G[v].push_back(edge(u, w));
}
dfs(1);
printf("%lld\n", lis.qmax(a[n]));
return 0;
}
UVA-8512 XOR
Description
Consider an array A with n elements. Each of its element is A[i] (1 ≤ i ≤ n). Then gives two integersQ, K, and Q queries follow. Each query, give you L, R, you can get Z by the following rules.
To get Z, at first you need to choose some elements from A[L] to A[R], we call them A[i1], A[i2],
. . . , A[it], Then you can get number Z = K or (A[i1], A[i2], . . . , A[it]).
Please calculate the maximum Z for each query .
Input
Several test cases.
First line an integer T (1 ≤ T ≤ 10). Indicates the number of test cases.
Then T test cases follows. Each test case begins with three integer N, Q, K (1 ≤ N ≤ 10000,1 ≤
Q ≤ 100000, 0 ≤ K ≤ 100000). The next line has N integers indicate A[1] to A[N] (0 ≤ A[i] ≤ 10^8).
Then Q lines, each line two integer L, R (1 ≤ L ≤ R ≤ N).
Output
For each query, print the answer in a single line.
Sample Input
1
5 3 0
1 2 3 4 5
1 3
2 4
3 5
Sample Output
3
7
7
题解
我们先考虑如何获得区间\([L,R]\)的\(k|(a[L] xor...a[R])\)最大值
考虑或运算,最终答案对于k的每一位如果有1则必然为1,所以对于区间\([L,R]\)的数,我们先不考虑其二进制下每一位与k同为1的情况,计算这种情况下的异或最大值,最后或上k就是答案
如何快速询问,有两种办法,较慢的方法是用线段树,维护区间的线性基,查询时暴力合并返回答案,可以通过此题,另一种方法将在下道题介绍
#include <bits/stdc++.h>
#define lson (o << 1)
#define rson (o << 1 | 1)
using namespace std;
typedef long long ll;
struct node {
ll b[40], p[40];
int cnt, flag;
node() {
memset(p, 0, sizeof(p));
memset(b, 0, sizeof(b));
cnt = flag = 0;
}
void clear() {
memset(p, 0, sizeof(p));
memset(b, 0, sizeof(b));
cnt = flag = 0;
}
bool insert(ll x) {
for (int i = 32; i >= 0; i--) {
if (x & (1LL << i)) {
if (b[i]) {
x ^= b[i];
}
else {
b[i] = x;
return true;
}
}
}
flag = 1;
return false;
}
ll qmax(ll base) {
ll res = base;
for (int i = 32; i >= 0; i--) {
if ((res ^ b[i]) > res) res ^= b[i];
}
return res;
}
ll qmin() {
if (flag) return 0;
for (int i = 0; i <= 32; i++) {
if (b[i]) return b[i];
}
return 0;
}
void rebuild() {
for (int i = 32; i >= 1; i--) {
if (b[i]) {
for (int j = i - 1; j >= 0; j--) {
if (b[i] & (1LL << j)) b[i] ^= b[j];
}
}
}
for (int i = 0; i <= 32; i++) {
if (b[i]) p[cnt++] = b[i];
}
}
ll kth(ll k) {
if (flag) --k;
if (k == 0) return 0;
ll res = 0;
if (k >= (1LL << cnt)) return -1;
for (int i = 0; i < cnt; i++) {
if (k & (1LL << i)) res ^= p[i];
}
return res;
}
};
node merge(node n1, node n2) {
node res = n1;
for (int i = 0; i <= 32; i++) {
if (n2.b[i]) res.insert(n2.b[i]);
}
res.flag = n1.flag | n2.flag;
return res;
}
const int N = 1e4 + 10;
node lis[N << 2];
ll rev;
ll a[N];
void pushup(int o) {
lis[o] = merge(lis[lson], lis[rson]);
}
void build(int o, int l, int r) {
if (l == r) {
lis[o].clear();
lis[o].insert(a[l] & rev);
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
pushup(o);
}
node query(int o, int l, int r, int ql, int qr) {
if (ql == l && r == qr) {
return lis[o];
}
int mid = (l + r) >> 1;
if (qr <= mid) return query(lson, l, mid, ql, qr);
else if (ql > mid) return query(rson, mid + 1, r, ql, qr);
else return merge(query(lson, l, mid, ql, mid), query(rson, mid + 1, r, mid + 1, qr));
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, q; ll k;
scanf("%d%d%lld", &n, &q, &k);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
rev = 0;
for (int i = 0; i <= 32; i++) {
if (((1LL << i) & k) == 0) rev += 1LL << i;
}
build(1, 1, n);
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
node ans = query(1, 1, n, l, r);
printf("%lld\n", k | ans.qmax(0));
}
}
return 0;
}
/*
1
5 3 0
1 2 3 4 5
1 3
2 4
3 5
*/
HDU-6579 Operation
Description
There is an integer sequence \(a\) of length \(n\) and there are two kinds of operations:
-
0 l r: select some numbers from \(a_l...a_r\) so that their xor sum is maximum, and print the maximum value.
-
1 x: append \(x\) to the end of the sequence and let \(n=n+1\).
Input
There are multiple test cases. The first line of input contains an integer \(T(T\le 10)\), indicating the number of test cases.
For each test case:
The first line contains two integers \(n, m(1\le n\le 5\times 10^5, 1\le m\le 5\times 10^5)\), the number of integers initially in the sequence and the number of operations.
The second line contains \(n\) integers \(a_1,a_2,...,a_n(0\le a_i< 2^{30})\), denoting the initial sequence.
Each of the next \(m\) lines contains one of the operations given above.
It's guaranteed that \(\sum n\le 10^6, \sum m\le 10^6 , 0\le x< 2^{30}\).
And operations will be encrypted. You need to decode the operations as follows, where lastans denotes the answer to the last type 0 operation and is initially zero:
For every type 0 operation, let l=(l xor lastans)mod n + 1, r=(r xor lastans)mod n + 1, and then swap(l, r) if l>r.
For every type 1 operation, let x=x xor lastans.
Output
For each type 0 operation, please output the maximum xor sum in a single line.
Sample Input
1
3 3
0 1 2
0 1 1
1 3
0 3 4
Sample Output
1
3
题解
首先这题强制在线,离线的方法就不要想了,我们考虑如何维护一个动态的可以插入的,并可以查询区间\([L,R]\)的异或最大值的线性基
首先,将原数组中的数从左到右插入到线性基中,每插入一个数都维护一个新的线性基,用\(b[MAXN][62]\)来保存
如插入原数组第i位时得到了[1,i]之间的线性基b[i][62],在插入第i+1位时首先复制第i位的线性基,然后将a[i+1]加入到\(b[i+1][62]\)中就直接得到了[1,i+1]之间的线性基
这样每次询问[l,r]之间的线性基时可以先直接得到[1,r]之间的线性基
如何在线性基中将[1,l-1]中插入的数排除掉?
首先,我们在将数字插入到线性基时同时维护一个\(pos[MAXN][62]\)数组,\(pos[i][j]\)保存插入a[i]后[1,i]之间的线性基第j位的数字是由原数组中哪个数字得到的
其次,在插入时我们要保证原数组中靠右插入的数字尽可能在线性基的高位出现
这样询问最大值的时候如果这个数在[L,R]的范围内,且能使异或和变大就加入答案即可
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 100;
int pos[N][32], b[N][32];
void insert(int x, int v) {
for (int i = 0; i <= 30; i++) {
pos[x][i] = pos[x - 1][i];
b[x][i] = b[x - 1][i];
}
int now = x;
for (int i = 30; i >= 0; i--) {
if (v & (1 << i)) {
if (!b[x][i]) {
b[x][i] = v;
pos[x][i] = now;
break;
}
if (pos[x][i] < now) {//让新插入的数尽量在高位
swap(b[x][i], v);
swap(pos[x][i], now);
}
v ^= b[x][i];//消元记得放外面,WA*1
}
}
}
int query(int l, int r) {
int ans = 0;
for (int i = 30; i >= 0; i--) {
if (pos[r][i] >= l && (ans ^ b[r][i]) > ans) ans ^= b[r][i];
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int v;
scanf("%d", &v);
insert(i, v);
}
int ans = 0;
for (int i = 1; i <= m; i++) {
int type;
scanf("%d", &type);
if (type == 0) {
int l, r;
scanf("%d%d", &l, &r);
l = (l ^ ans) % n + 1;
r = (r ^ ans) % n + 1;
if (l > r) swap(l, r);
ans = query(l, r);
printf("%d\n", ans);
}
else {
int x;
scanf("%d", &x);
n++;
x ^= ans;
insert(n, x);
}
}
}
return 0;
}
BZOJ-2844 albus就是要第一个出场
Description
已知一个长度为n的正整数序列A(下标从1开始), 令 S = { x | 1 <= x <= n }, S 的幂集2^S定义为S 所有子集构成的集合。定义映射 f : 2^S -> Zf(空集) = 0f(T) = XOR A[t] , 对于一切t属于T现在albus把2^S中每个集合的f值计算出来, 从小到大排成一行, 记为序列B(下标从1开始)。 给定一个数, 那么这个数在序列B中第1次出现时的下标是多少呢?
Input
第一行一个数n, 为序列A的长度。接下来一行n个数, 为序列A, 用空格隔开。最后一个数Q, 为给定的数.
Output
共一行, 一个整数, 为Q在序列B中第一次出现时的下标模10086的值.
Sample Input
3
1 2 3
1
Sample Output
3
Hint
样例解释: N = 3, A = [1 2 3] S = {1, 2, 3} 2^S = {空, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}} f(空) = 0 f({1}) = 1 f({2}) = 2 f({3}) = 3 f({1, 2}) = 1 xor 2 = 3 f({1, 3}) = 1 xor 3 = 2 f({2, 3}) = 2 xor 3 = 1 f({1, 2, 3}) = 0 所以 B = [0, 0, 1, 1, 2, 2, 3, 3]
数据范围:
1 <= N <= 10,0000
其他所有输入均不超过10^9
题解
有一个结论,如果线性基中最后有cnt个元素,原本有n个元素插入了线性基,空集异或算作0的话,那么每个元素都出现了\(2^{n-cnt}\)
这样我们直接看一个数是第几大的数计算一下答案即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll pp = 10086;
int n;
ll qpow(ll a, int b) {
ll ans = 1;
while (b) {
if (b & 1) ans = ans * a % pp;
a = a * a % pp;
b >>= 1;
}
return ans % pp;
}
struct node {
ll b[65], p[65];
int cnt, flag;
node() {
memset(p, 0, sizeof(p));
memset(b, 0, sizeof(b));
cnt = flag = 0;
}
bool insert(ll x) {
for (int i = 62; i >= 0; i--) {
if (x & (1LL << i)) {
if (b[i]) {
x ^= b[i];
}
else {
b[i] = x;
return true;
}
}
}
flag = 1;
return false;
}
ll qmax() {
ll res = 0;
for (int i = 62; i >= 0; i--) {
if ((res ^ b[i]) > res) res ^= b[i];
}
return res;
}
ll qmin() {
if (flag) return 0;
for (int i = 0; i <= 62; i++) {
if (b[i]) return b[i];
}
return 0;
}
void rebuild() {
for (int i = 62; i >= 1; i--) {
if (b[i]) {
for (int j = i - 1; j >= 0; j--) {
if (b[i] & (1LL << j)) b[i] ^= b[j];
}
}
}
for (int i = 0; i <= 62; i++) {
if (b[i]) p[cnt++] = b[i];
}
}
ll kth(ll k) {
if (flag) --k;
if (k == 0) return 0;
ll res = 0;
if (k >= (1LL << cnt)) return -1;
for (int i = 0; i < cnt; i++) {
if (k & (1LL << i)) res ^= p[i];
}
return res;
}
ll rankth(ll k) {
ll rankk = 0;
for (int i = 0; i < cnt; i++) {
ll tmp = p[i];
ll bit = 0;
while (tmp) {
bit++;
tmp >>= 1;
}
bit--;
if (1LL & (k >> bit))
rankk = (rankk + (1LL << i)) % pp;
}
return (rankk * qpow(2, n - cnt) % pp + 1LL) % pp;
}
} lis;
node merge(node n1, node n2) {
node res = n1;
for (int i = 0; i <= 62; i++) {
if (n2.b[i]) res.insert(n2.b[i]);
}
res.flag = n1.flag | n2.flag;
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
ll x;
scanf("%lld", &x);
lis.insert(x);
}
lis.rebuild();
ll q; scanf("%lld", &q);
printf("%lld\n", lis.rankth(q));
return 0;
}