首页 > 代码库 > [luogu P3797] 妖梦斩木棒 [线段树]

[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 42 1 42 2 41 2 (2 2 4
输出样例#1:
101

说明

对于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 }

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

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