[luogu P3797] 妖梦斩木棒 [线段树]

题目背景

妖梦是住在白玉楼的半人半灵,拥有使用剑术程度的能力。

题目描述

有一天,妖梦正在练习剑术。地面上摆放了一支非常长的木棒,妖梦把它们切成了等长的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


 

十分可爱的线段树题啊QAQ

造一棵线段树,维护

        1.有几段完整的木棍,

        2.左边是否有向右边的开口,

        3.右边是否有向左边的开口,

        4.以及是否完全无开口(全为'X')(便于区间合并)。

区间合并想得有点乱,但是不用下传标记的单点修改还是很exciting的。

一开始想特判一下n=1的情况来着,后来想想算了吧。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 using namespace std;
  5 
  6 inline int rint(){
  7     char ch;
  8     int re=0;
  9     bool flag=0;
 10     while((ch=getchar())!='-'&&(ch<'0'||ch>'9'));
 11     ch=='-'?flag=1:re=ch-'0';
 12     while((ch=getchar())>='0'&&ch<='9')  re=re*10+ch-'0';
 13     return flag?-re:re;
 14 }
 15 
 16 inline char rchar(){
 17     char ch;
 18     while((ch=getchar())!='X'&&ch!='('&&ch!=')');
 19     return ch;
 20 }
 21 
 22 struct segment{
 23     int l,r,num;
 24     bool ll,rr,xx;
 25     segment(){  num=0;  ll=0;  rr=0;  xx=0;  }
 26 };
 27 
 28 const int maxn=200001;
 29 
 30 segment tre[maxn<<2];
 31 int n,m;
 32 
 33 segment merge(const segment &tl,const segment &tr){
 34     segment tx;
 35     tx.l=tl.l;  tx.r=tr.r;
 36     tx.xx=tl.xx&tr.xx;
 37     tx.ll=tr.xx?tl.ll:tr.ll;
 38     tx.rr=tl.xx?tr.rr:tl.rr;
 39     tx.num=tl.num+tr.num+((tl.ll&tr.rr)?1:0);
 40     return tx;
 41 }
 42 
 43 void push_up(int x){
 44     tre[x]=merge(tre[x<<1],tre[x<<1|1]);
 45 }
 46 
 47 void build(int x,int l,int r){
 48     tre[x].l=l;  tre[x].r=r;
 49     if(l==r){
 50         if(l==1)  tre[x].ll=1;
 51         else  if(r==n)  tre[x].rr=1;
 52         else  tre[x].xx=1;
 53         return;
 54     }
 55     int mid=(l+r)>>1;
 56     build(x<<1,l,mid);  build(x<<1|1,mid+1,r);
 57     push_up(x);
 58 }
 59 
 60 void change(int x,int pos,int chaa){
 61     if(tre[x].l==tre[x].r){
 62         if(chaa==0){
 63             tre[x].ll=0;
 64             tre[x].rr=0;
 65             tre[x].xx=1;
 66         }
 67         else  if(chaa==1){
 68             tre[x].ll=1;
 69             tre[x].rr=0;
 70             tre[x].xx=0;
 71         }
 72         else{
 73             tre[x].ll=0;
 74             tre[x].rr=1;
 75             tre[x].xx=0;
 76         }
 77         return;
 78     }
 79     int mid=(tre[x].l+tre[x].r)>>1;
 80     if(pos<=mid)  change(x<<1,pos,chaa);
 81     else  change(x<<1|1,pos,chaa);
 82     push_up(x);
 83 }
 84 
 85 segment query(int x,int L,int R){
 86     if(L<=tre[x].l&&tre[x].r<=R)  return tre[x];
 87     int mid=(tre[x].l+tre[x].r)>>1;
 88     if(R<=mid)  return query(x<<1,L,R);
 89     if(L>mid)  return query(x<<1|1,L,R);
 90     return merge(query(x<<1,L,mid),query(x<<1|1,mid+1,R));
 91 }
 92 
 93 int main(){
 94     //freopen("temp.in","r",stdin);
 95     n=rint();  m=rint();
 96     build(1,1,n);
 97     int opt,pos,left,right,chaa;
 98     char cha;
 99     for(int i=0;i<m;i++){
100         opt=rint();
101         switch(opt){
102             case 1:{
103                 pos=rint();  cha=rchar();
104                 if(cha=='X')  chaa=0;
105                 else  if(cha=='(')  chaa=1;
106                 else  chaa=2;
107                 change(1,pos,chaa);
108                 break;
109             }
110             case 2:{
111                 left=rint();  right=rint();
112                 printf("%d\n",query(1,left,right).num);
113                 break;
114             }
115         }
116     }
117     return 0;
118 }

我听过空境的回音
雨水浇绿孤山岭
听过被诅咒的秘密
没听过你

转载于:https://www.cnblogs.com/ZYBGMZL/p/6971461.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值