BZOJ4568 [线性基][树链剖分][线段树]

Description

A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。

Solution

看到求异或最大值,是可以用线性基的,想到线性基是可以合并的,就可以用树链剖分加线段树维护。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 20202;
typedef long long ll;

inline char get(void) {
  static char buf[100000], *S = buf, *T = buf;
  if (S == T) {
    T = (S = buf) + fread(buf, 1, 100000, stdin);
    if (S == T) return EOF;
  }
  return *S++;
}
template<typename T>
inline void read(T &x) {
  static char c; x = 0;
  for (c = get(); c < '0' || c > '9'; c = get());
  for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

struct LB {
  ll a[65];
  inline void Clear(void) {
    memset(a, 0, sizeof a);
  }
  inline LB(void) {
    Clear();
  }
  inline ll Insert(ll x) {
    for (int i = 60; i >= 0; i--)
      if ((1ll << i) & x) {
        if (!a[i]) {
          a[i] = x; break;
        }
        x ^= a[i];
      }
    return x;
  }
  inline ll &operator [](int x) {
    return a[x];
  }
  inline ll Max(void) {
    ll mx = 0;
    for (int i = 60; i >= 0; i--)
      if ((mx ^ a[i]) > mx) mx ^= a[i];
    return mx;
  }
};
struct edge {
  int to, next;
  edge(int t = 0, int n = 0):to(t), next(n) {}
};
edge G[N << 1];
int head[N];
ll val[N];
int fa[N], dep[N], size[N], top[N], pos[N], son[N];
int Gcnt, Pcnt, n, q, x, y, f1, f2;
LB T[N << 3];
LB ans;

inline void AddEdge(int from, int to) {
  G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
  G[++Gcnt] = edge(from, head[to]); head[to] = Gcnt;
}
inline LB Merge(LB &a, LB &b) {
  LB c = a;
  for (int i = 60; i >= 0; i--)
    if (b[i]) c.Insert(b[i]);
  return c;
}
void dfs1(int u) {
  size[u] = 1;
  for (int i = head[u]; i; i = G[i].next) {
    if (fa[u] == G[i].to) continue;
    dep[G[i].to] = dep[u] + 1;
    fa[G[i].to] = u; dfs1(G[i].to);
    size[u] += size[G[i].to];
    if (size[son[u]] < size[G[i].to]) son[u] = G[i].to;
  }
}
void dfs2(int u, int t) {
  top[u] = t; pos[u] = ++Pcnt;
  if (son[u]) dfs2(son[u], t);
  for (int i = head[u]; i; i = G[i].next)
    if (son[u] != G[i].to && fa[u] != G[i].to)
      dfs2(G[i].to, G[i].to);
}
void Insert(int o, int l, int r, int pos, ll v) {
  if (l == r) return (void)(T[o].Insert(v));
  int mid = (l + r) >> 1;
  if (pos <= mid) Insert(o << 1, l, mid, pos, v);
  else Insert(o << 1 | 1, mid + 1, r, pos, v);
  T[o] = Merge(T[o << 1], T[o << 1 | 1]);
}
void Query(int o, int l, int r, int L, int R) {
  if (l >= L && r <= R) return (void)(ans = Merge(T[o], ans));
  int mid = (l + r) >> 1; LB res;
  if (L <= mid) Query(o << 1, l, mid, L, R);
  if (R > mid) Query(o << 1 | 1, mid + 1, r, L, R);
}

int main(void) {
  read(n); read(q);
  for (int i = 1; i <= n; i++) read(val[i]);
  for (int i = 1; i < n; i++) {
    read(x); read(y);
    AddEdge(x, y);
  }
  dfs1(1); dfs2(1, 1);
  for (int i = 1; i <= n; i++)
    Insert(1, 1, n, pos[i], val[i]);
  while (q--) {
    read(x); read(y); ans.Clear();
    f1 = top[x]; f2 = top[y];
    while (f1 != f2) {
      if (dep[f1] < dep[f2]) {
        swap(x, y); swap(f1, f2);
      }
      Query(1, 1, n, pos[f1], pos[x]);
      x = fa[f1]; f1 = top[x];
    }
    if (dep[x] > dep[y]) swap(x, y);
    Query(1, 1, n, pos[x], pos[y]);
    printf("%lld\n", ans.Max());
  }
  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值