题解:P13274 [NOI2025] 三目运算符
bianshiyang · · 题解
题目传送门
提供一个需要用线段树维护
分析
复盘了我赛时的思路,感觉这题出的挺自然的,没有什么难想的点。
O(qn^2)
因为一定有
特殊性质 A
相当于是一直在对字符串做右移操作,直至字符串是全
O(qn) +特殊性质 B
这个时候需要分析一下
而且我们会发现若字符串出现若干个合法子串,那么我们只需要考虑第一个合法子串即可,因为它的答案不会被更前面的字符的变换所影响,毕竟答案只跟
此时就会有人有疑问那万一第一个合法子串之前的字符变成“不动的”次数比第一个合法子串的答案要更大怎么办呢?其实我们会发现若一个字符串不存在合法子串,那么此时答案不会超过
闲话:笔者开始没有对两者取
这样我们就可以
O(q\sqrt n) +特殊性质 C
赛时以为如果会了特殊性质 C 就可以拼上回滚莫队做到
不过特殊性质
至于分块做法是赛后 lhy 告诉我的,貌似块内暴力找合法位置比线段树好实现的多。
O(q\log n)
我们只关注字符串中的第一个连续超过两个 但是我的实现相当丑。
代码实现
对于每个变量都给了注释,不过赛后复盘又打了一遍自己考场上的史山代码很难受。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10;
int C,T,n,q;
ll ans;
char a[N];
//这个结构体是存储0或者1对应的变量,以1为注释,0也是类似的
struct node{
bool pd,ok;//pd表示是否存在合法串,ok表示是否存在101
int op,nh,nt;//op表示第一个合法串中第一个0的出现位置,nh表示区间开头连续1的数量,nt表示区间结尾连续1的数量
void cl(){pd=ok=0;op=nh=nt=0;}//初始化函数
};
struct Seg{
node c[2];//c[0]存0相关信息,c[1]存1相关信息
int f,l,r,b1,b2,e2,e1;//f是标记,l和r表示区间的范围,b1、b2表示区间开头两个字符(b1是第一个b2是第二个),e1、e2表示区间结尾两个字符(e1是倒数第一个,e2是倒数第二个),不存在用-1表示
void cl(){c[0].cl(),c[1].cl(),f=0,b1=b2=e1=e2=-1;}//初始化函数
void tt(int _b1,int _b2,int _e2,int _e1){b1=_b1,b2=_b2,e2=_e2,e1=_e1;}//赋值函数
void tag()
{
f^=1;
if(b1!=-1) b1^=1;
if(b2!=-1) b2^=1;
if(e2!=-1) e2^=1;
if(e1!=-1) e1^=1;
swap(c[0],c[1]);
}//区间翻转函数
friend Seg operator+(Seg x,Seg y)
{
Seg res;res.cl();
int len1=x.r-x.l+1,len2=y.r-y.l+1;
for(int i=0;i<2;i++)
{
node &rr=res.c[i];
node xx=x.c[i],yy=y.c[i];
if(xx.pd&&xx.op<=x.r) rr.pd=1,rr.op=xx.op;//合法串出现在左边
else if(xx.nt+yy.nh>1) rr.pd=1,rr.op=y.l+yy.nh;//拼在一起出现合法串
else if(yy.pd) rr.pd=1,rr.op=yy.op;//合法串出现在右边
rr.nh=xx.nh+(xx.nh==len1?yy.nh:0);
rr.nt=yy.nt+(yy.nt==len2?xx.nt:0);
rr.ok=xx.ok|yy.ok;//左右是否存在101或者010
if(len1>=2&&x.e2==i&&x.e1==(i^1)&&y.b1==i) rr.ok=1;
if(len2>=2&&x.e1==i&&y.b1==(i^1)&&y.b2==i) rr.ok=1;//拼在一起是否出现101或010
}
if(len1==1)
{
if(len2==1) res.tt(x.b1,y.b1,x.b1,y.b1);
else res.tt(x.b1,y.b1,y.e2,y.e1);
}
else
{
if(len2==1) res.tt(x.b1,x.b2,x.e1,y.e1);
else res.tt(x.b1,x.b2,y.e2,y.e1);
}//这里要分类讨论,因为不一定存在b2和e2
res.l=x.l,res.r=y.r;
return res;
}//区间合并函数
}tr[N<<2];
void pushup(int k)
{
int f=tr[k].f;
tr[k]=tr[k<<1]+tr[(k<<1)|1];
tr[k].f=f;
}
void down(int k)
{
if(!tr[k].f) return;
tr[k<<1].tag(),tr[(k<<1)|1].tag();
tr[k].f=0;
}
void build(int k,int l,int r)
{
tr[k].cl(),tr[k].l=l,tr[k].r=r;
if(l==r)
{
int ch=a[l]-'0';
tr[k].b1=tr[k].e1=ch;
tr[k].c[ch].nh=tr[k].c[ch].nt=1;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid),build((k<<1)|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r,int a,int b)
{
if(a<=l&&b>=r)
{
tr[k].tag();
return;
}
down(k);
int mid=(l+r)>>1;
if(a<=mid) update(k<<1,l,mid,a,b);
if(b>mid) update((k<<1)|1,mid+1,r,a,b);
pushup(k);
}
void solve(int i)
{
node res=tr[1].c[1];
int k=0;
if(res.op) k=max(k,n-res.op+1);
if(res.ok) k=max(k,1);//记得取max
ans^=1ll*(i+1)*k;
}
int main()
{
scanf("%d%d",&C,&T);
while(T--)
{
scanf("%d%d%s",&n,&q,a+1);
a[n+1]='2';ans=0;
build(1,1,n);solve(0);
for(int _i=1,l,r;_i<=q;_i++)
{
scanf("%d%d",&l,&r);
update(1,1,n,l,r);solve(_i);
}
printf("%lld\n",ans);
}
return 0;
}