1. 程式人生 > >妖夢斬木棒

妖夢斬木棒

輸入輸出 sign xxxxx %d ostream scan 並且 合並 print

題目背景

妖夢是住在白玉樓的半人半靈,擁有使用劍術程度的能力。

題目描述

有一天,妖夢正在練習劍術。地面上擺放了一支非常長的木棒,妖夢把它們切成了等長的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

線段樹

對於每一個區間,維護是否向左開c[rt].ls(右括號左邊無木棍),是否向右開c[rt].rs(左括號右邊無棍)

再維護是否為空c[rt].ps(用處:合並時,c[rt*2].ps&&c[rt*2+1].ls→c[rt].ls=1)

還維護完整木棍數才c[rt].sum;合並時c[rt*2].rs&&c[rt*2+1].ls→c[rt].sum=c[rt*2].sum+c[rt*2+1].sum+1;

註意查詢時,不能直接返回c[rt].sum並求和。因為會多算或少算。要返回所有維護信息,再把解合並

如查詢1-7(紅色為返回c[rt].sum的節點),ans=1,但明顯ans應等於2,應為返回時忽略了左右單獨括號的合並

(xx)()(xx)

(xx)( )(xx)

)(x x)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct Messi
{
    int ls,rs,sum,ps;
}c[800001];
int n,m;
Messi merge(Messi a,Messi b)
{
    Messi x;
    x.ps=a.ps&b.ps;
    x.ls=a.ps?b.ls:a.ls;
    x.rs=b.ps?a.rs:b.rs;
    x.sum=a.sum+b.sum;
    if (b.ls&&a.rs&&!b.ps&&!a.ps) x.sum++;    
    return x;
}
void build(int rt,int l,int r)
{
    if (l==r)
     {
         if (l==1)
         {
          c[rt].rs=1;
          c[rt].ps=0;
          c[rt].ls=0;
         }
         else if (r==n)
         {
          c[rt].rs=0;
          c[rt].ps=0;
          c[rt].ls=1;
         }
         else 
         {
             c[rt].rs=1;
             c[rt].ls=1;
             c[rt].ps=1;
         }
    return;
     }     
    int mid=(l+r)/2;
    build(rt*2,l,mid);
    build(rt*2+1,mid+1,r);    
    c[rt]=merge(c[rt*2],c[rt*2+1]);
}
void update(int rt,int l,int r,int x,int sign)
{
    if (l==r)
    {
        if (sign==1)
         {
          c[rt].rs=1;
          c[rt].ps=0;
          c[rt].ls=0;
         }
         else if (sign==0)
         {
          c[rt].rs=0;
          c[rt].ps=0;
          c[rt].ls=1;
         }
         else 
         {
             c[rt].rs=1;
             c[rt].ls=1;
             c[rt].ps=1;
         }
         return;
    }
    int mid=(l+r)/2;
     if (x<=mid) update(rt*2,l,mid,x,sign);
     else update(rt*2+1,mid+1,r,x,sign);
    c[rt]=merge(c[rt*2],c[rt*2+1]);
}
Messi query(int rt,int l,int r,int L,int R)
{
   if (l>=L&&r<=R)
   {
       return c[rt];
   }
   int mid=(l+r)/2;
    if (R<=mid) return query(rt*2,l,mid,L,R);
    if (L>mid) return query(rt*2+1,mid+1,r,L,R);
   return merge(query(rt*2,l,mid,L,R),query(rt*2+1,mid+1,r,L,R));
}
int main()
{int i,j,k,x,y;
char ch;
    cin>>n>>m;
    build(1,1,n);
     for (i=1;i<=m;i++)
     {
         scanf("%d",&k);
          if (k==2)
          {
              scanf("%d%d",&x,&y);
              //cout<<‘p‘<<x<<‘ ‘<<y<<endl;
              printf("%d\n",query(1,1,n,x,y).sum);
         }
         else 
         {
             int sign;
             scanf("%d %c",&x,&ch);
             //cout<<‘q‘<<x<<‘ ‘<<ch<<endl;
              if (ch==()
                sign=1;
                else if (ch==))sign=0;
                else sign=2;
              update(1,1,n,x,sign);
         }
     }
}

妖夢斬木棒