【线段树提高】51nod &&洛谷

两个线段树很好的题目

NO.1 51nod  1426 沙拉酱括号

点击这里即可

好多都用前缀和+二分 我觉得线段树可行做了一下  就是要用vc++ 交C语言 不然会t

#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<cstring>
//using namespace std;
#define maxn 8000000+100
int aa[maxn];
int min(int x,int y){
  if(x>=y) return y;
  return x;
}
struct ac{
  int s,l,r;
  ac(){
    s=0;l=0,r=0;
  }
  ac(int a,int b,int c){
     s=a,l=b,r=c;
  }
}a[maxn*4];
void build(int l,int r,int in){
  if(l==r){
     if(aa[l]==1)
      a[in]=ac(0,1,0);
     else a[in]=ac(0,0,1);
     return ;
  }
  int mid=(l+r)/2;
  build(l,mid,in*2);
  build(mid+1,r,in*2+1);
  int lson=in*2,rson=in*2+1;

  int x=min(a[lson].l,a[rson].r);

  int y=a[lson].l-x;int z=a[rson].r-x;

  int s=a[lson].s+a[rson].s+x;

  a[in]=ac(s,y+a[rson].l,a[lson].r+z);
}
ac query(int l,int r,int L,int R,int in){
   if(l==L&&r==R){
      return a[in];
   }
   int mid=(l+r)/2;
   if(L>mid){         //这个地方一定要处理好 因为我返回的是结构体所以返回的含有这个区间左右括号的信息
      return query(mid+1,r,L,R,in*2+1);
   }else if(R<=mid){
      return query(l,mid,L,R,in*2);
   }
   ac b=query(l,mid,L,mid,in*2);

   ac c=query(mid+1,r,mid+1,R,in*2+1);

   int x=min(b.l,c.r);

   int y=b.l-x;int z=c.r-x;

   int k=b.s+c.s+x;

   return ac(k,y+c.l,b.r+z);
}
int main(){
   char s[maxn*10];
   scanf("%s",s);
   int n;
   scanf("%d",&n);
   int len=strlen(s);
   for(int j=0;j<len;j++){
      if(s[j]=='(')
       aa[j+1]=1;
      else aa[j+1]=-1;
   }
   build(1,len,1);
   for(int j=0;j<n;j++){
      int l,r;
      scanf("%d%d",&l,&r);
      ac b=query(1,len,l,r,1);
      printf("%d\n",b.s*2);
   }

   return 0;
}

洛谷 3797 妖梦斩木棒

点击这里即可

#include<bits/stdc++.h>
using namespace std;
#define maxn 600000+100//结构体消耗内存好大 我开了这么大才可以 小的话后面的数据全部 re
struct ac{
  int s,l,r; //(l=='('  r==')';
  ac(){
    s=0;
    l=0;
    r=0;
  }
  ac(int a,int b,int c){
     s=a;
     l=b;
     r=c;
  }
}a[maxn*4];
int n,m;
void updata(int l,int r,int in,int va,int v){
   if(r<1|r>n) return ;
   if(l==r){
      if(v==1) a[in]=ac(0,1,0);  
      if(v==2) a[in]=ac(0,0,1);
      if(v==3) a[in]=ac(0,0,0); //更新不多解释
      return ;
   }
   int mid=(l+r)/2;
   if(va>mid){
      updata(mid+1,r,in*2+1,va,v);
   }else{
      updata(l,mid,in*2,va,v);
   }
   int x=a[in*2].s+a[in*2+1].s;
   int y=a[in*2].l+a[in*2+1].r;            // 看左右能否组成一个完整的括号
   if(y==2){
     a[in]=ac(x+1,a[in*2+1].l,a[in*2].r); //可以组成采用右儿子的左括号,左儿子的右括号
   }else{
     if(x!=0){  //当他们存在完整的括号对时 
       if(a[in*2].s==0&&a[in*2].r==0){    // 完整的括号对在右区间的时候
          a[in]=ac(x,a[in*2+1].l,a[in*2+1].r);
       }else if(a[in*2+1].s==0&&a[in*2+1].l==0){  //在左区间的时候
          a[in]=ac(x,a[in*2].l,a[in*2].r);
       }
       else a[in]=ac(x,a[in*2+1].l,a[in*2].r); //左右区间都存在
     }
     else a[in]=ac(x,a[in*2+1].l|a[in*2].l,a[in*2+1].r|a[in*2].r);//左右都不存在
   }
}
ac query(int l,int r,int L,int R,int in){
   if(l==L&&r==R){
      return a[in];
   }
   int mid=(l+r)/2;
   if(L>mid){         //这个地方一定要处理好 因为我返回的是结构体所以返回的含有这个区间左右括号的信息
      return query(mid+1,r,L,R,in*2+1);
   }else if(R<=mid){
      return query(l,mid,L,R,in*2);
   }        
   //下面和更新操作的一样 
   ac b=query(l,mid,L,mid,in*2);
   ac c=query(mid+1,r,mid+1,R,in*2+1);
   int x=b.s+c.s;
   int y=b.l+c.r;
   if(y==2){
     return ac(x+1,c.l,b.r);
   }
   if(x!=0){
       if(b.s==0&&b.r==0){
          return ac(x,c.l,c.r);
       }else if(c.s==0&&c.l==0){
          return ac(x,b.l,b.r);
       }
       return ac(x,a[in*2+1].l,a[in*2].r);
     }
   return ac(x,c.l|b.l,c.r|b.r);
}
int main(){
 // FILE *fp;
 // fp=fopen("C://Users//Dcoder//Desktop//1.txt","r+");
   cin>>n>>m;
   //build(1,n,1);
   updata(1,n,1,1,1);//相当于两次更新 一次开头 一次结尾
   updata(1,n,1,n,2);
   while(m--){
      int x;
      cin>>x;
      if(x==2){
         int l,r;
         cin>>l>>r;
       //  fprintf(fp,"%d\n",query(1,n,l,r,1).s);
         cout<<query(1,n,l,r,1).s<<endl;
      }else{
         char c;
         int y;
         cin>>y>>c;
         if(c=='X'){
            updata(1,n,1,y,3);
         }else if(c=='('){
            updata(1,n,1,y,1);
         }else{
            updata(1,n,1,y,2);
         }
      }
   }
   //fclose(fp);
   return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值