线性基学习笔记

线性基就解决子集异或问题,没了,可以最大最小,k大k小。

就是你插入不超过 \(\log a_i\) 个数大概能得到最大值。
假设你事先插入过 \(p_{i_1},p_{i_2}……,p_{i_k}\)
那么你插入一个 \(j , (j \leq i\{\})\)
实质上插入的是 \(x\oplus p_{i_1} \oplus p_{i_2}……\)
易证 \(2^i \leq p_i \leq 2^{(i+1)}\)
所以可以倒过来循环
至于怎么证明不知道,反正这样求最值是对的。

代码长这个样子

P3812 【模板】线性基

// by Isaunoya
#include 
using namespace std;

#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define Rep(i, x, y) for (register int i = (x); i >= (y); --i)
#define int long long

const int _ = 1 << 21;
struct I {
  char fin[_], *p1 = fin, *p2 = fin;
  inline char gc() {
    return (p1 == p2) && (p2 = (p1 = fin) + fread(fin, 1, _, stdin), p1 == p2) ? EOF : *p1++;
  }
  inline I& operator>>(int& x) {
    bool sign = 1;
    char c = 0;
    while (c < 48) ((c = gc()) == 45) && (sign = 0);
    x = (c & 15);
    while ((c = gc()) > 47) x = (x << 1) + (x << 3) + (c & 15);
    x = sign ? x : -x;
    return *this;
  }
  inline I& operator>>(double& x) {
    bool sign = 1;
    char c = 0;
    while (c < 48) ((c = gc()) == 45) && (sign = 0);
    x = (c - 48);
    while ((c = gc()) > 47) x = x * 10 + (c - 48);
    if (c == '.') {
      double d = 1.0;
      while ((c = gc()) > 47) d = d * 0.1, x = x + (d * (c - 48));
    }
    x = sign ? x : -x;
    return *this;
  }
  inline I& operator>>(char& x) {
    do
      x = gc();
    while (isspace(x));
    return *this;
  }
  inline I& operator>>(string& s) {
    s = "";
    char c = gc();
    while (isspace(c)) c = gc();
    while (!isspace(c) && c != EOF) s += c, c = gc();
    return *this;
  }
} in;
struct O {
  char st[100], fout[_];
  signed stk = 0, top = 0;
  inline void flush() { fwrite(fout, 1, top, stdout), fflush(stdout), top = 0; }
  inline O& operator<<(int x) {
    if (top > (1 << 20)) flush();
    if (x < 0) fout[top++] = 45, x = -x;
    do
      st[++stk] = x % 10 ^ 48, x /= 10;
    while (x);
    while (stk) fout[top++] = st[stk--];
    return *this;
  }
  inline O& operator<<(char x) {
    fout[top++] = x;
    return *this;
  }
  inline O& operator<<(string s) {
    if (top > (1 << 20)) flush();
    for (char x : s) fout[top++] = x;
    return *this;
  }
} out;

#define pb emplace_back
#define fir first
#define sec second

int n, a[64], p[64];
signed main() {
#ifdef _WIN64
  freopen("testdata.in", "r", stdin);
#endif
  in >> n;
  rep(i, 1, n) in >> a[i];
  rep(i, 1, n) {
    for (int j = 50; ~j; --j) {
      if ((a[i] & (1ll << j))) {
        if (!p[j]) p[j] = a[i];
        a[i] ^= p[j];
      }
    }
  }
  int ans = 0;
  for (int i = 50; ~i; --i) ans = max(ans, ans ^ p[i]);
  out << ans << '\n';
  return out.flush(), 0;
}

P4151 [WC2011]最大XOR和路径

找个出所有简单环,这样就可以和原有路径抵消掉
最后求异或,没了

// by Isaunoya
#include 
using namespace std;

#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define Rep(i, x, y) for (register int i = (x); i >= (y); --i)
#define int long long

const int _ = 1 << 21;
struct I {
  char fin[_], *p1 = fin, *p2 = fin;
  inline char gc() {
    return (p1 == p2) && (p2 = (p1 = fin) + fread(fin, 1, _, stdin), p1 == p2) ? EOF : *p1++;
  }
  inline I& operator>>(int& x) {
    bool sign = 1;
    char c = 0;
    while (c < 48) ((c = gc()) == 45) && (sign = 0);
    x = (c & 15);
    while ((c = gc()) > 47) x = (x << 1) + (x << 3) + (c & 15);
    x = sign ? x : -x;
    return *this;
  }
  inline I& operator>>(double& x) {
    bool sign = 1;
    char c = 0;
    while (c < 48) ((c = gc()) == 45) && (sign = 0);
    x = (c - 48);
    while ((c = gc()) > 47) x = x * 10 + (c - 48);
    if (c == '.') {
      double d = 1.0;
      while ((c = gc()) > 47) d = d * 0.1, x = x + (d * (c - 48));
    }
    x = sign ? x : -x;
    return *this;
  }
  inline I& operator>>(char& x) {
    do
      x = gc();
    while (isspace(x));
    return *this;
  }
  inline I& operator>>(string& s) {
    s = "";
    char c = gc();
    while (isspace(c)) c = gc();
    while (!isspace(c) && c != EOF) s += c, c = gc();
    return *this;
  }
} in;
struct O {
  char st[100], fout[_];
  signed stk = 0, top = 0;
  inline void flush() { fwrite(fout, 1, top, stdout), fflush(stdout), top = 0; }
  inline O& operator<<(int x) {
    if (top > (1 << 20)) flush();
    if (x < 0) fout[top++] = 45, x = -x;
    do
      st[++stk] = x % 10 ^ 48, x /= 10;
    while (x);
    while (stk) fout[top++] = st[stk--];
    return *this;
  }
  inline O& operator<<(char x) {
    fout[top++] = x;
    return *this;
  }
  inline O& operator<<(string s) {
    if (top > (1 << 20)) flush();
    for (char x : s) fout[top++] = x;
    return *this;
  }
} out;

#define pb emplace_back
#define fir first
#define sec second

template 
inline void cmax(T& x, const T& y) {
  (x < y) && (x = y);
}
template 
inline void cmin(T& x, const T& y) {
  (x > y) && (x = y);
}

int n, m;
const int N = 5e4 + 10;
const int M = 1e5 + 10;
int cnt = 0, head[N];
struct Edge {
  int v, nxt, w;
} e[M << 1];
int d[N], p[65];
bool vis[N];
void add(int u, int v, int w) {
  e[++cnt] = { v, head[u], w }, head[u] = cnt;
  e[++cnt] = { u, head[v], w }, head[v] = cnt;
}
void insert(int val) {
  for (int i = 63; ~i; --i)
    if (val & (1ll << i)) {
      if (!p[i]) p[i] = val;
      val ^= p[i];
    }
}
void dfs(int u, int cur) {
  d[u] = cur, vis[u] = 1;
  for (int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].v;
    if (vis[v])
      insert(cur ^ e[i].w ^ d[v]);
    else
      dfs(v, cur ^ e[i].w);
  }
}
int query(int val) {
  int res = val;
  for (int i = 63; ~i; --i) cmax(res, res ^ p[i]);
  return res;
}
signed main() {
#ifdef _WIN64
  freopen("testdata.in", "r", stdin);
#endif
  in >> n >> m;
  int u, v, w;
  while (m--) {
    in >> u >> v >> w, add(u, v, w);
  }
  dfs(1, 0);
  out << query(d[n]) << '\n';
  return out.flush(), 0;
}

留一些坑。

洛谷P3857 [TJOI2008]彩灯
洛谷P4301 [CQOI2013]新Nim游戏
CF895C Square Subsets
洛谷P4570 [BJWC2011]元素
洛谷P3265 [JLOI2015]装备购买
洛谷P3292 [SCOI2016]幸运数字
洛谷P4151 [WC2011]最大XOR和路径
CF724G Xor-matic Number of the Graph
CF938G Shortest Path Queries

你可能感兴趣的:(线性基学习笔记)