【Codeforces Gym 100739 A】Queries

题意:给\(n\)个数\(a_{1..n}\),以及\(m\)个询问,每个询问如下:

  • 1 p x表示把第\(p\)位上的数改成\(x\)
  • 2 a b表示找出\(\sum_{a\le l\le r\le b}a_l\ xor\ \ldots\ xor\ a_r\)

思路:线段树。

首先肯定把位拆开来考虑,那么我们建\(10\)棵线段树。

每棵线段树需要维护什么呢?

首先肯定需要维护这一段中这一位的亦或和。

那么我们知道亦或就是自己的逆运算,即\(xorsum(a,b)=xorPrefixSum(b)\ xor\ xorPrefixSum(a-1)\),所以我们需要存储这一段所有的前缀亦或和中为\(1\)\(0\)的。

同时肯定要把答案记录下来。

那么我们的线段树的每个节点长这样:

  • sum,即亦或和
  • odd,即前缀亦或和中为1的个数
  • even,即前缀亦或和中为0的个数
  • ans,即答案。

然后我们看看上推操作。

首先我们的sum=ls.sum^rs.sum

那么我们分ls.sum的情况讨论。

如果ls.sum=0,那么我们的rs.odd会加到odd中,rs.even也是同理。

看ans会有什么变化。

首先我们的ans肯定要从ls.ans和rs.ans中来,然后rs中的even可以和所有的ls中的odd配对,odd也同理。

如果ls.sum=1就应该把所有的rs.even换成odd,odd换成even。

那么我们在查询的时候用zkw线段树的方式,从左边向上走一串,右边走一串,分别"上推"成\(l\)\(r\),最后把\(l\)\(r\)合并即可。

还是不太好写的吧。还有一种想法就是存前后缀的和中有多少\(0\)\(1\)。这样可能自然一点。毕竟和比差总是好的嘛。

猜你喜欢

转载自www.cnblogs.com/denverjin/p/10850369.html