写给 H 题,用了和正解看上去完全不同的思路并且 A 掉了 QAQ

题目大意

给定一个长为 nn 的只含 AB 两种字符的字符串。字符串中的每个字符表示对给定二进制数 tt 的一个操作,字符 A 表示将 tt 中的所有元素 0,10,1 取反,B 表示令 tt 增加 11

进行 QQ 次询问,第 ii 次询问对给定二进制数 tit_i 顺序进行字符串中区间 [li,ri][l_i,r_i] 里的所有操作,输出操作后形成的二进制数,与给定的 tit_i 位数相同,即溢出的高位舍弃。

数据范围 2e52e5 级别,询问强制在线。

思路

考虑对一段区间中的操作进行合并。

我们可以将操作 B+1+1 和给定的被操作二进制数 tit_i 都在逻辑上增添符号位 11,即将它们都视为负数。由于我们只输出操作后二进制数的有效后缀,该行为不会影响答案的正确性。

对每个区间 [l,r][l,r],我们将其中所有的操作压为一个三元组 (x,y,z)(x,y,z),表示将“对 tt 顺序执行字符串中区间 [li,ri][l_i,r_i] 里的所有操作”等价为顺次进行以下三个操作:

  1. tt 增加 xx
  2. 如果 y=1y=1 则令 tt 变成 tt 的补码。
  3. 如果 z=1z=1 则令 tt 中的所有元素 0,10,1 取反。

让我们用数学归纳法来证明上述等价是可行的。

首先由三元组的定义可以知道,操作 A 可以表示成三元组 (0,0,1)(0,0,1),操作 B 可以表示成三元组 (1,0,0)(1,0,0)

假设两个相邻的操作区间分别可以用上述三元组表示为 (x1,y1,z1)(x1,y1,z1)(x2,y2,z2)(x2,y2,z2),即我们要顺序进行如下操作:

  1. tt 增加 x1x_1
  2. 如果 y1=1y_1=1 则令 tt 变成 tt 的补码。
  3. 如果 z1=1z_1=1 则令 tt 中的所有元素 0,10,1 取反。
  4. tt 增加 x2x_2
  5. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  6. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

怎样将这六个操作合并为一个三元组呢?我们进行如下讨论:

如果 y1=0y_1=0z1=0z_1=0,相当于顺序进行下述操作:

  1. tt 增加 x1x_1
  2. tt 增加 x2x_2
  3. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  4. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

等价为三元组 (x1+x2,y2,z2)(x_1+x_2,y_2,z_2)

如果 y1=0y_1=0z1=1z_1=1,相当于顺序进行下述操作:

  1. tt 增加 x1x_1
  2. tt 中的所有元素 0,10,1 取反。
  3. tt 增加 x2x_2
  4. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  5. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

观察操作 2,32,3,又我们将所有操作数都视为负数,可以发现:

(t)+x2=[[(t)]+[x2]]=[[(t)]+1+(x2)+1]=[t+(x2)+2]\begin{aligned} &(\sim t)+x_2\\ =&[[(\sim t)]_补+[x_2]_补]_补\\ =&[[\sim(\sim t)]+1+(\sim x_2)+1]_补\\ =&[t+(\sim x_2)+2]_补 \end{aligned}

所以操作等价于:

  1. tt 增加 x1x_1
  2. tt 增加 (x2)+2(\sim x_2)+2
  3. tt 变成 tt 的补码。
  4. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  5. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

等价为三元组 (x1+(x2)+2,y21,z2)(x_1+(\sim x_2)+2,y_2\oplus 1,z_2)

如果 y1=1y_1=1z1=0z_1=0,相当于顺序进行下述操作:

  1. tt 增加 x1x_1
  2. tt 变成 tt 的补码。
  3. tt 增加 x2x_2
  4. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  5. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

观察操作 2,32,3,又我们将所有操作数都视为负数,可以发现:

[t]+x2=[[[t]]+[x2]]=[t+[x2]]=[t+(x2)+1]\begin{aligned} &[t]_补+x_2\\ =&[[[t]_补]_补+[x_2]_补]_补\\ =&[t+[x_2]_补]_补\\ =&[t+(\sim x_2)+1]_补 \end{aligned}

所以操作等价于:

  1. tt 增加 x1x_1
  2. tt 增加 (x2)+1(\sim x_2)+1
  3. tt 变成 tt 的补码。
  4. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  5. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

则等价为三元组 (x1+(x2)+1,y21,z2)(x_1+(\sim x_2)+1,y_2\oplus 1,z_2)

如果 y1=1y_1=1z1=1z_1=1,相当于顺序进行下述操作:

  1. tt 增加 x1x_1
  2. tt 变成 tt 的补码。
  3. tt 中的所有元素 0,10,1 取反。
  4. tt 增加 x2x_2
  5. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  6. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

由过去两种方案的讨论可知,操作 3,43,4 可以合并为先令 tt 增加 (x2)+2(\sim x_2)+2,再令 tt 变成 tt 的补码。然后我们将 (x2)+2(\sim x_2)+2 求补后可以交换和操作 22 的执行顺序。而两次求补操作可以相互抵消,则最终相当于顺序执行下述操作:

  1. tt 增加 x1x_1
  2. tt 增加 (((x2)+2))+1(\sim((\sim x_2)+2))+1
  3. 如果 y2=1y_2=1 则令 tt 变成 tt 的补码。
  4. 如果 z2=1z_2=1 则令 tt 中的所有元素 0,10,1 取反。

等价为三元组 (x1+(((x2)+2))+1,y2,z2)(x_1+(\sim((\sim x_2)+2))+1,y_2,z_2)

综上所述,我们证明了可以用三元组表示一段操作区间,同时证明了相邻的两个操作区间可以 O(1)O(1) 进行合并。用线段树进行维护即可。

代码

#include <bits/stdc++.h>
using namespace std;
using LL=unsigned long long;
const int N=2e5+5;
unsigned int m,n;
struct asdf{
	LL x;
	unsigned int y,z;
	asdf operator + (const asdf a) const
	{
		if (y==0&&z==0) return {x+a.x,a.y,a.z};
		if (y&&z) return {x+(~((~a.x)+2))+1,a.y,a.z};
		if (y) return {x+(~a.x)+1,!a.y,a.z};
		return {x+(~a.x)+2,!a.y,a.z};
	}
}s[N<<2];
char a[N];
void build(unsigned int l,unsigned int r,unsigned int rt)
{
	if (l==r)
	{
		if (a[l]=='A') s[rt].z=1;
		else s[rt].x=1;
		return;
	}
	build(l,(l+r)>>1,rt<<1);
	build((l+r>>1)+1,r,rt<<1|1);
	s[rt]=s[rt<<1]+s[rt<<1|1];
}
asdf query(unsigned int l,unsigned int r,unsigned int rt,unsigned int x,unsigned int y)
{
	if (x<=l&&r<=y) return s[rt];
	if (y<=(l+r>>1)) return query(l,l+r>>1,rt<<1,x,y);
	if (x>(l+r>>1)) return query((l+r>>1)+1,r,rt<<1|1,x,y);
	return query(l,l+r>>1,rt<<1,x,y)+query((l+r>>1)+1,r,rt<<1|1,x,y);
}
char ch[101];
LL val[101];
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i) scanf(" %c",&a[i]);
	build(1,n,1);
	LL x,ans=0,L,R;
	for (int i=1,k,l,r;i<=m;++i)
	{
		scanf("%llu%llu %s",&L,&R,ch+1);
		l=min((ans^L)%n+1,(ans^R)%n+1);
		r=max((ans^L)%n+1,(ans^R)%n+1);
		asdf opt=query(1,n,1,l,r);
		k=strlen(ch+1);
		for (int j=0;j<k;++j) val[j]=ch[k-j]-'0';
		val[0]+=opt.x;
		for (int j=0;j<k;j++)
		{
			val[j+1]+=val[j]/2;
			val[j]%=2;
		}
		if (opt.y)
		{
			for (int j=0;j<k;j++) val[j]=!val[j];
			val[0]++;
			for (int j=0;j<k;j++)
			{
				val[j+1]+=val[j]/2;
				val[j]%=2;
			}
		}
		if (opt.z) for (int j=0;j<k;j++) val[j]=!val[j];
		ans=0;
		for (int j=k-1;j>=0;--j)
		{
			ans=ans<<1|val[j];
			printf("%d",val[j]);
		}
		printf("\n");
	}
}