码迷,mamicode.com
首页 > 其他好文 > 详细

CF703D Mishka and Interesting sum

时间:2020-11-02 10:11:25      阅读:19      评论:0      收藏:0      [点我收藏+]

标签:ORC   n+1   扫描线   compare   lin   位置   表示   space   小技巧   

知识点:线段树

原题面:Codeforces


题意简述

给定一长度为 \(n\) 的数列 \(a\),给定 \(m\) 次询问。
每次询问给定区间内,出现过偶数次的权值 的异或和。
\(1\le n,m\le 10^6\)\(1\le a_i\le 10^9\)


分析题意

根据异或的自反性,一个区间内所有数的异或和,为区间内出现过 奇数次 的权值的异或和。
再根据异或的自反性,一个区间的答案,即为区间出现过的权值的异或和,异或上区间内出现过 奇数次 的权值的异或和。

即有下式:

\[ans (l,r) = \left(\bigoplus_{i=l}^{r} a_i\right) \oplus \left(\bigoplus_{k\in \{a_{i}\}} k\right) \]

显然区间内所有数的异或和可前缀和维护。
考虑如何求得区间内出现过的权值的异或和。


维护的信息不便于删除,套路地考虑扫描线。
令线段树下标为 \(r\) 的叶节点维护区间 \([l,r]\) 的信息。
其中 \(l\) 为当前扫描到的左端点。

维护 \(nex_{i}\),表示 \(a_i\) 右侧等于 \(a_i\) 的第一个数,若不存在则为 \(n+1\)
则对于一个位置 \(i\),其影响的范围为 \([i,nex_i)\),他的出现令该区间权值的异或和 \(\oplus a_i\)

初始 \(l=1\) 时,按上述方法将所有位置插入线段树中,构造出初始状态。
\(l+1\) 时,删去了 \(a_{l-1}\) 显然仅有 \([l, nex_{l-1})\) 部分受到影响,根据异或的自反性,令该区间异或和 \(\oplus a_{l-1}\) 即可。
\(l\) 右移的同时回答询问。


线段树支持区间异或即可。
单次 \(l+1\),单次查询的复杂度均为 \(O(\log n)\)
总复杂度 \(O((n+m)\log n)\)


爆零小技巧

注意修改区间的开闭情况。


代码实现

//知识点:线段树 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <map>
#define LL long long
const int kMaxn = 1e6 + 10;
//=============================================================
struct Query {
  int l, r, id;
} q[kMaxn];
int n, m, a[kMaxn], nex[kMaxn], sum[kMaxn], ans[kMaxn];
std::map <int, int> pos;
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == ‘-‘) f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ ‘0‘);
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
bool CompareQuery(Query fir, Query sec) {
  if (fir.l != sec.l) return fir.l < sec.l;
  return fir.r < sec.r;
}
namespace Seg {
  #define ls (now_<<1)
  #define rs (now_<<1|1)
  #define mid ((L_+R_)>>1)
  int sum[kMaxn << 2], tag[kMaxn << 2];
  void Pushdown(int now_) {
    if (! tag[now_]) return ;
    sum[ls] ^= tag[now_]; tag[ls] ^= tag[now_];
    sum[rs] ^= tag[now_]; tag[rs] ^= tag[now_];
    tag[now_] = 0;
  }  
  void Modify(int now_, int L_, int R_, int l_, int r_, int val_) {
    if (l_ <= L_ && R_ <= r_) {
      sum[now_] ^= val_;
      tag[now_] ^= val_;
      return ;
    }
    Pushdown(now_);
    if (l_ <= mid) Modify(ls, L_, mid, l_, r_, val_);
    if (r_ > mid) Modify(rs, mid + 1, R_, l_, r_, val_);
  }
  int Query(int now_, int L_, int R_, int pos_) {
    if (L_ == R_) return sum[now_];
    Pushdown(now_);
    if (pos_ <= mid) return Query(ls, L_, mid, pos_);
    return Query(rs, mid + 1, R_, pos_);
  }
      
  void Debug(int now_, int L_, int R_) {
    if (L_ == R_) {
      printf("%d ", sum[now_]);
      return ;
    }
    Pushdown(now_);
    Debug(ls, L_, mid);
    Debug(rs, mid + 1, R_);
  }
  #undef ls
  #undef rs
  #undef mid
}
void Prepare() {
  n = read();
  for (int i = 1; i <= n; ++ i) {
    a[i] = read();
    sum[i] = sum[i - 1] ^ a[i];
  }
  for (int i = n; i >= 1; -- i) {
    if (pos.count(a[i])) {
      nex[i] = pos[a[i]]; 
    } else {
      nex[i] = n + 1;
    }
    pos[a[i]] = i;
  }
  for (int i = 1; i <= n; ++ i) {
    Seg::Modify(1, 1, n, i, nex[i] - 1, a[i]);
  }
  
  m = read();
  for (int i = 1; i <= m; ++ i) {
    q[i] = (Query) {read(), read(), i};
  }
  std::sort(q + 1, q + m + 1, CompareQuery);
}
//=============================================================
int main() {
  Prepare();
  int i = 1;
  for (; i <= m; ++ i) {
    if (q[i].l > 1) break;
    ans[q[i].id] = sum[q[i].r] ^ sum[q[i].l - 1] ^ Seg::Query(1, 1, n, q[i].r);
  }
  for (int l = 2; l <= n; ++ l) {
    Seg::Modify(1, 1, n, l, nex[l - 1] - 1, a[l - 1]);
    for (; i <= m; ++ i) {
      if (q[i].l > l) break;
      ans[q[i].id] = sum[q[i].r] ^ sum[q[i].l - 1] ^ Seg::Query(1, 1, n, q[i].r);
    }
  }
  for (int i = 1; i <= m; ++ i) {
    printf("%d\n", ans[i]);
  }
  return 0;
}

CF703D Mishka and Interesting sum

标签:ORC   n+1   扫描线   compare   lin   位置   表示   space   小技巧   

原文地址:https://www.cnblogs.com/luckyblock/p/13836657.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!