题意:给\(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\)。这样可能自然一点。毕竟和比差总是好的嘛。