生成线性基:
void cal() {
for (int i = 0; i < n; ++i)
for (int j = MAX_BASE; j >= 0; --j)
if (a[i] >> j & 1) {
if (b[j]) a[i] ^= b[j];
else {
b[j] = a[i];
for (int k = j - 1; k >= 0; --k) if (b[k] && (b[j] >> k & 1)) b[j] ^= b[k];
for (int k = j + 1; k <= MAX_BASE; ++k) if (b[k] >> j & 1) b[k] ^= b[j];
break;
}
}
模板题
传送们:https://codeforces.com/problemsets/acmsguru/problem/99999/275
VJ 的 SGU 接口好像炸了,在 Codeforces 上找到了这道题。记得MAX_BASE要开在63,一开始WA因为MAX_BASE开小了。
#include <iostream>
#define LL long long
using namespace std;
const int maxn=150,BASE=63;
LL a[maxn],b[maxn],n;
void cal(){
for (int i=0;i<n;i++)
for (int j = BASE;j>=0;--j)
if (a[i] >> j & 1){
if (b[j]) a[i]^=b[j];
else {
b[j] = a[i];
for (int k=j-1;k>=0;--k)
if (b[k] && (b[j]>>k &1)) b[j]^= b[k];
for (int k = j+1;k<=BASE;++k)
if (b[k] >> j & 1) b[k]^=b[j];
break;
}
}
}
int main(){
cin >> n;
for (int i=0;i<n;i++) cin >> a[i];
cal();
LL ans=0;
for (int i=0;i<=BASE;i++) //cout << b[i] <<' ',
ans=ans^b[i];
cout << ans;
}
区间异或和第k小
求第k小,把k用二进制编码,非常巧妙的标表达出第k小的数选择。
原理比较好理解,关键是注意如果n=size的情况下放在一起可能可以取零,我们选择用一个 zero = n !=size 来标记。这是一开始 WA 的原因,可是写的跟标程自认为没啥区别了还是会 WA,于是直接粘一波标程吧。
// HDOJ 3949 线性基
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll readLL() {
static ll n;
static int ch;
n = 0, ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
return n;
}
const int MAX_N = 100000 + 3, MAX_BASE = 60;
int n, zero = false;
ll a[MAX_N], b[MAX_BASE + 3];
vector<ll> mmap;
void prepare() {
int cnt = 0;
memset(b, 0, sizeof b);
for (int i = 0; i < n; ++i)
for (int j = MAX_BASE; j >= 0; --j)
if (a[i] >> j & 1) {
if (b[j]) a[i] ^= b[j];
else {
b[j] = a[i], cnt++;
for (int k = j - 1; k >= 0; --k) if (b[k] && ((b[j] >> k) & 1)) b[j] ^= b[k];
for (int k = j + 1; k <= MAX_BASE; ++k) if ((b[k] >> j) & 1) b[k] ^= b[j];
break;
}
}
zero = cnt != n;
mmap.clear();
for (int i = 0; i <= MAX_BASE; ++i)
if (b[i]) mmap.push_back(b[i]);
}
ll query(ll k) {
if (zero) k--;
if (k >= (1LL << (int)mmap.size())) return -1;
ll ans = 0;
for (int i = 0; i < (int)mmap.size(); ++i) if ((k >> i) & 1)
ans ^= mmap[i];
return ans;
}
int main() {
int caseNum = readLL();
for (int t = 1; t <= caseNum; ++t) {
n = readLL();
for (int i = 0; i < n; ++i) a[i] = readLL();
prepare();
printf("Case #%d:\n", t);
int q = readLL();
for (int i = 0; i < q; ++i) printf("%lld\n", query(readLL()));
}
return 0;
}
无向图1-n路径边上异或和最大
涉及到比较多的技巧。直接放上代码。
// Created by Sengxian on 2016/12/05.
// Copyright (c) 2016年 Sengxian. All rights reserved.
// BZOJ 2115 线性基
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll readLL() {
static ll n;
static int ch;
n = 0, ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
return n;
}
const int MAX_N = 50000 + 3, MAX_M = 100000 + 3, MAX_BASE = 60;
struct edge {
edge *next, *rev;
int to;
ll cost;
edge(edge *next = NULL, int to = 0, ll cost = 0): next(next), to(to), cost(cost) {}
} pool[MAX_M * 2], *pit = pool, *first[MAX_N];
int n, m, cnt = 0;
ll d[MAX_N], a[MAX_N + MAX_M * 2], b[MAX_BASE + 3];
void dfs(int u, edge *fa) {
static bool vis[MAX_N];
vis[u] = true;
for (edge *e = first[u]; e; e = e->next) if (e->rev != fa) {
if (!vis[e->to]) {
d[e->to] = d[u] ^ e->cost;
dfs(e->to, e);
} else a[cnt++] = d[u] ^ d[e->to] ^ e->cost;
}
}
void prepare() {
for (int i = 0; i < cnt; ++i)
for (int j = MAX_BASE; j >= 0; --j)
if (a[i] >> j & 1) {
if (b[j]) a[i] ^= b[j];
else {
b[j] = a[i];
for (int k = j - 1; k >= 0; --k) if (b[k] && (b[j] >> k & 1)) b[j] ^= b[k];
for (int k = j + 1; k <= MAX_BASE; ++k) if (b[k] >> j & 1) b[k] ^= b[j];
break;
}
}
}
int main() {
n = readLL(), m = readLL();
for (int i = 0; i < m; ++i) {
int u = readLL() - 1, v = readLL() - 1;
ll w = readLL();
first[u] = new (pit++) edge(first[u], v, w);
first[v] = new (pit++) edge(first[v], u, w);
first[u]->rev = first[v], first[v]->rev = first[u];
}
dfs(0, NULL);
prepare();
ll ans = d[n - 1];
for (int i = MAX_BASE; i >= 0; --i)
if (ans < (ans ^ b[i])) ans ^= b[i];
printf("%lld\n", ans);
return 0;
}