P10856 【MX-X2-T5】「Cfz Round 4」Xor-Forces 题解

P10856 【MX-X2-T5】「Cfz Round 4」Xor-Forces

给定一个长度为 \(n=2^k\) 的数组 \(a\),下标从 \(0\) 开始,维护 \(m\) 次操作:

  1. 给定 \(x\),设数列 \(a'\) 满足 \(a'_i=a_{i\oplus x}\),将 \(a\) 修改为 \(a'\)。其中 \(\oplus\) 表示按位异或运算。
  2. 给定 \(l,r\),查询 \(a\) 的下标在 \(l,r\) 之间的子数组有多少颜色段。不保证 \(\bm {l\le r}\),若 \(\bm{l > r}\),请自行交换 \(\bm{l,r}\)

其中,一个极长的所有数都相等的子数组称为一个颜色段。
强制在线。
\(T \in \{ 0, 1 \}\)\(0\le k\le 18\)\(n=2^k\)\(1\le m\le 2\times 10^5\)\(1\le a_i\le n\)\(\mathit{op} \in \{ 1, 2 \}\)\(0\le x,l,r < n\)

思路

乍一看操作1几乎完全无法操作。但是自己手动模拟几组数据或者打表可以发现一些有趣的事实:

  1. 操作1有结合律,因此询问事实上等价于询问一个区间根据操作1操作后的颜色段数,其中操作1的 \(x\) 是之前所有操作的前缀异或和。

  2. 以2的次幂为长度的区间(即类似于线段树各层上的区间)无论异或的数是多少,这个区间内的数一定仍在长度相等的区间里,只是顺序可能变动。
    这个结论是比较好感性理解的。对于线段树上的一段区间,其区间的序号的二进制一定是一段高位相等而剩下的低位连续。
    这些序号同时异或上一个数后高位仍然对位相同而低位仍覆盖一段相对应的区间。

  3. 对于一段区间,其异或上的值只有小于其长度的才有用。这里的“有用”指的是没办法在线 \(O(1)\) 算出来区间对应的地方,必须预处理的。

因此仍然考虑类似于2的想法,对于区间里的所有序号,如果改变其高位,那么实际上与另一段高位改变后的区间异或上低位的值是等同的。
因此只需要对于每个区间预处理出其 \(0\)\(len-1\) 的异或后的答案,就可以 \(O(1)\) 回答询问了。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int T,k,m,a[N],n;
vector <int> f[N<<2];
struct node
{
	#define ls (u<<1)
	#define rs ((u<<1)|1)
	void build(int u,int l,int r)
	{
		int len=r-l+1,mid=(l+r)>>1;f[u].resize(len);
		if(l==r){f[u][0]=0;return;}
		build(ls,l,mid),build(rs,mid+1,r);
		for(int x=0;x<len;x++)
		f[u][x]=f[ls][x%(len/2)]+f[rs][x%(len/2)]+(a[mid^x]==a[(mid+1)^x]?1:0);
	}
	int query(int u,int l,int r,int ql,int qr,int x)
	{
		int mid=(l+r)>>1,len=r-l+1,ceng=__lg(r-l+1),newl=l^(x>>ceng<<ceng),newu=u+(newl-l)/len,newx=x%len,res=0;
		if(ql<=l&&qr>=r) return f[newu][newx];
		if(ql<=mid) res+=query(ls,l,mid,ql,qr,x);
		if(qr>mid) res+=query(rs,mid+1,r,ql,qr,x);
		if(ql<=mid&&qr>mid) res+=a[mid^x]==a[(mid+1)^x]?1:0;
		return res;
	}
}ds;
int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>T>>k>>m;n=1<<k;
	for(int i=0;i<n;i++) cin>>a[i];
	ds.build(1,0,n-1);
	int lst=0,sumx=0;
	for(int i=1,op,l,r,t;i<=m;i++)
	{
		cin>>op;if(op==1) {cin>>t,t=t^(T*lst),sumx=sumx^t;continue;}
		cin>>l>>r;l=l^(T*lst),r=r^(T*lst);if(l>r) swap(l,r);lst=r-l+1-ds.query(1,0,n-1,l,r,sumx);cout<<lst<<'\n';
	}
}
posted @ 2024-11-13 15:02  all_for_god  阅读(10)  评论(0)    收藏  举报