[十二省联考2019] 异或粽子

[十二省联考2019] 异或粽子

题意:

题目传送门

题解:

没有做过异或之超级钢琴,但是这几道题的做法似乎还是非常好像的。首先做前缀异或和,这样问题转化成了个给定序列,找出\(K\)对数字对\((i, j)\)使这几对数字的异或的值之和最大。考虑如果我们确定右端点\(r\),那么异或最大值是很好确定的,直接在\(Trie\)上查找即可。假如我们要查找第\(K\)大的异或值的话,就可以使用可持久化\(Trie\)树。虽然以前没有写过,但写法就和主席树差不多。每当找到\(r\)为右端点的最大值之后,把这个异或值删除,再次查找的就是第二大的了,以此类推就可以得到第\(K\)大的了。

然后我们用堆维护每一个点为右端点时的异或最大值,然后每次取出堆顶的值,更新次大值即可。总共会修改\(K\)次,时间复杂度为\(KlogK\),空间复杂度为\(KlogK\),跑的似乎非常慢。。

Code:

#pragma GCC optimize(2, "inline", "Ofast")
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6 + 50;
typedef long long ll;

int n, k;
ll a[N], s[N], nrt[N];
ll res;

namespace PerTrie {
  int rt[N], cnt[N << 3], ch[N << 3][2];
  int tot = 0;
  queue<int> rec;
  void Init() { for(int i = 1; i < (N << 3); i++) rec.push(i); }
  int Newnode() { int o = rec.front(); rec.pop(); cnt[o] = 0; ch[o][0] = ch[o][1] = 0; return o; }

  void Insert(int &o, int rt, ll x, int Bit) {
    if(!o || o == rt) o = Newnode();
    cnt[o] = cnt[rt] + 1; ch[o][0] = ch[rt][0]; ch[o][1] = ch[rt][1];
    if(Bit == -1) return ;
    int c = (1ll << Bit) & x ? 1 : 0;
    Insert(ch[o][c], ch[rt][c], x, Bit - 1);
    return ;
  }

  void Delete(int &o, int rt, ll x, int Bit) {
    if(!o || o == rt) o = Newnode();
    cnt[o] = cnt[rt] - 1; ch[o][0] = ch[rt][0]; ch[o][1] = ch[rt][1];
    if(Bit == -1) {
      if(cnt[o] == 0) rec.push(o), o = 0;
      return ;
    }
    int c = (1ll << Bit) & x ? 1 : 0;
    Delete(ch[o][c], ch[rt][c], x, Bit - 1);
    if(cnt[o] == 0) rec.push(o), o = 0;
    return ;
  }

  void Query(int o, ll x, int Bit) {
    if(Bit == -1) return ;
    int c = (1ll << Bit) & x ? 1 : 0;
    if(ch[o][c ^ 1]) Query(ch[o][c ^ 1], x, Bit - 1), res ^= (1ll << Bit);
    else Query(ch[o][c], x, Bit - 1);
  }
}

struct node {
  int R;
  ll val;
  bool operator < (const node &rhs) const {
    return val < rhs.val;
  };
};

priority_queue<node> Q;

int main() {
  scanf("%d%d", &n, &k);
  PerTrie::Init();
  for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
  for(int i = 1; i <= n; i++) s[i] = s[i - 1] ^ a[i];
  PerTrie::Insert(PerTrie::rt[0], PerTrie::rt[n + 1], 0, 33);
  for(int i = 1; i <= n; i++) PerTrie::Insert(PerTrie::rt[i], PerTrie::rt[i - 1], s[i], 33), nrt[i] = i;
  for(int i = 1; i <= n; i++) {
    res = 0;
    PerTrie::Query(PerTrie::rt[i - 1], s[i], 33);
    Q.push( (node) { i, res });
  }
  
  ll ans = 0;
  int tot = n + 1;

  while(k--) {
    node o = Q.top(); Q.pop();
    ans += o.val;
    ll V = o.val ^ s[o.R];
    PerTrie::Delete(PerTrie::rt[++tot], PerTrie::rt[nrt[o.R - 1]], V, 33);
    nrt[o.R - 1] = tot;
    if(PerTrie::rt[nrt[o.R - 1]] == 0) continue; 
    res = 0;
    PerTrie::Query(PerTrie::rt[nrt[o.R - 1]], s[o.R], 33);
    Q.push( (node) { o.R, res } );
  }
  
  printf("%lld\n", ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Apocrypha/p/10666886.html