题目背景
妖梦是住在白玉楼的半人半灵,拥有使用剑术程度的能力。
题目描述
有一天,妖梦正在练习剑术。地面上摆放了一支非常长的木棒,妖梦把它们切成了等长的n段。现在这个木棒可以看做由三种小段构成,中间的n-2段都是左右都被切断的断头,我们记做’X’,最左边的一段和最右边的一段各有一个圆头,记做’(‘和’)’。幽幽子吃饱后闲来无事,决定戏弄一下妖梦。她拿来了许多这样的三种小段木棒,来替换掉妖梦切下来的n段中的一部分,然后问妖梦一些问题。这些操作可以这样描述:
1 x C 将第x个小段的木棒替换成C型,C只会是’X’,’(‘,’)’中的一种
2 l r 询问妖梦从第l段到第r段之间(含l,r),有多少个完整的木棒
完整的木棒左右两端必须分别为’(‘和’)’,并且中间要么什么都没有,要么只能有’X’。
虽然妖梦能够数清楚这些问题,但幽幽子觉得她回答得太慢了,你能教给妖梦一个更快的办法吗?
输入格式
第一行两个整数n,m,n表示共有n段木棒,m表示有m次操作。
木棒的初始形状为(XXXXXX......XXXXXX)。
接下来m行,每行三个整数/字符,用空格隔开。第一个整数为1或2,表示操作的类型,若类型为1,则接下来一个整数x,一个字符C。若类型为2,接下来两个整数l,r。含义见题目描述。
输出格式
对于每一个操作2,输出一行一个整数,表示对应询问的答案。
输入输出样例
输入 #1复制
4 4 2 1 4 2 2 4 1 2 ( 2 2 4
输出 #1复制
1 0 1
说明/提示
对于30%的数据,1<=n,m<=1000
对于100%的数据,1<=n,m<=200000
by-orangebird
上代码:
#include<cstdio>
#include<cstdlib>
#include<ctype.h>
#define ls (now<<1)
#define rs ((now<<1)+1)
const int MARX = 2e5+10;
//=============================================================
struct node
{
int L,R;//维护的区间,
bool allX,side[3];//是否全为X , side[0]:最左侧是否有) , side[1]:最右侧是否有( ,
int sum,pos[3]; //区间内完整木棒数, 及区间内 最左侧),最右侧( 位置
}tree[MARX<<4];
int n,m, map[110],map1[3];//map存映射关系, 0<->( , 1<->) , 2<->X
char now_list[MARX];//当前的 字串
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
void Build(int now,int l,int r)//常规建树
{
tree[now].L=l,tree[now].R=r;
if(l == r) return ;
int mid = (l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
}
void pushup(int now)//更新 第now个节点 的各信息
{
//维护 区间最左侧 是否有)
if(tree[ls].side[0]) tree[now].side[0]=1, tree[now].pos[0] = tree[ls].pos[0];
else if(!tree[ls].allX && tree[rs].side[0]) tree[now].side[0]=1 , tree[now].pos[0]= tree[rs].pos[0];
else tree[now].side[0] = 0;
//维护 区间最右侧 是否有(
if(tree[rs].side[1]) tree[now].side[1]=1 , tree[now].pos[1] = tree[rs].pos[1];
else if(!tree[rs].allX && tree[ls].side[1]) tree[now].side[1]=1 , tree[now].pos[1]= tree[ls].pos[1];
else tree[now].side[1] = 0;
//更新区间 合法字串数, 区间是否全为'X'
tree[now].sum = tree[ls].sum+tree[rs].sum + (tree[ls].side[1] && tree[rs].side[0]);;
tree[now].allX = (tree[ls].allX || tree[rs].allX);
}
void Change(int now,int pos,int value) //单点修改, 将第pos个位置修改为 value
{
if(tree[now].L==pos && tree[now].R==pos)//当前区间 即指定位置
{
now_list[pos] = map1[value];//更新各信息
tree[now].allX = (value!=2);//是否全为X
tree[now].side[0] = tree[now].side[1] = 0;//更新 最左,最右侧元素
tree[now].side[value] = 1, tree[now].pos[value]=pos;
return ;
}
int mid = (tree[now].L+tree[now].R)>>1;
if(pos <= mid) Change(ls,pos,value);
if(pos > mid) Change(rs,pos,value);
pushup(now);//更新当前位置
}
int Inquiry(int now,int L,int R)//查询区间 L,R 的目标字串数
{
if(L <= tree[now].L && tree[now].R <= R) return tree[now].sum;
int sum=0,flag=0;//flag 判断区间 是否同时被左右子区间更新过
int mid = (tree[now].L+tree[now].R)>>1;
if(L<=mid) sum += Inquiry(ls,L,R), flag++;
if(R>mid) sum += Inquiry(rs,L,R), flag++;
//存在 合法字串 横跨左右子区间
if(flag == 2) sum += (tree[ls].side[1] && L<=tree[ls].pos[1] && tree[rs].side[0] && R>=tree[rs].pos[0]);
return sum;
}
void prepare()//预处理
{
n=read() , m=read();
Build(1,1,n);
Change(1,1,1) , Change(1,n,0);
now_list[1]='(' , now_list[n]=')';//原始字串
for(int i=2; i<n; i++) now_list[i]='X';
map['(']=1, map[')']=0, map['X']=2;//建立映射关系
map1[1]='(',map1[0]=')',map1[2]='X';
}
//=============================================================
signed main()
{
prepare();
for(int i=1; i<=m; i++)
{
int type=read(),value1,value2;
char value3;
if(type == 1) //修改操作
{
value1=read(); value3 = getchar();//读入防卡
getchar();
if(now_list[value1] == value3) continue;
Change(1,value1,map[(int)(value3)]);
}
else //查询操作
{
value1=read(), value2=read();
printf("%d\n",Inquiry(1,value1,value2));
}
}
}