线段树树状数组小结

7-14 Yet Another Bracket Sequence

给定一个序列,m次改变序列,问序列是否括号匹配。

把(看做+1,)看做-1,那么括号匹配的条件即为1到n的区间最小前缀和为0,且pre[ n ] 为0,用线段树模拟即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+500;
int pre[N];
struct node{
  int s,l,r,lz;
}tree[N<<2];
void push_up(int x){
  tree[x].s=min(tree[x<<1].s,tree[x<<1|1].s);
}
void push_down(int x){
  if(!tree[x].lz)return ;
  tree[x<<1].s+=tree[x].lz,tree[x<<1|1].s+=tree[x].lz;
  tree[x<<1].lz+=tree[x].lz,tree[x<<1|1].lz+=tree[x].lz;
  tree[x].lz=0;
}

void build(int x,int l,int r){
  tree[x].l=l;tree[x].r=r;
  if(l==r){
    tree[x].s=pre[l];
    return ;
  }
  int mid=(l+r)>>1;
  build(x<<1,l,mid);
  build(x<<1|1,mid+1,r);
  push_up(x);
}

void modify(int x,int l,int r,int val){
    if(tree[x].l>=l&&tree[x].r<=r){
      tree[x].lz+=val;
      tree[x].s+=val;
      return ;
    }  
    push_down(x);
    int mid=(tree[x].l+tree[x].r)>>1;
    if(l<=mid)modify(x<<1,l,r,val);
    if(r>mid)modify(x<<1|1,l,r,val);
    push_up(x);
}
char s[N];
int main(){
    int n,m;
    scanf("%d %d %s",&n,&m,s+1);
    pre[0]=0;
    for(int i=1;i<=n;i++){
      if(s[i]=='(')pre[i]=pre[i-1]+1;
      else pre[i]=pre[i-1]-1;
    }
    
    // for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(s[i]=='(')?1:-1;
    
    int sum=pre[n];
    build(1,1,n);
    
    while(m--){
      int x;scanf("%d",&x);

      if(s[x]=='(')s[x]=')',sum-=2,modify(1,x,n,-2);
      else s[x]='(',sum+=2,modify(1,x,n,2);
      // cout<<"test : "<<tree[1].s<<" "<<sum<<endl;
      if(tree[1].s==0&&sum==0)puts("Yes");
      else puts("No");
    }

    // system("pause");
  return 0;
}
View Code

Can you answer these queries III

单点修改和询问区间最大子串和,设置  lmax 和 rmax,合并即可。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+500;
typedef long long ll;
const int inf=1e9;
struct node{
  int l,r;
  int sum,val,lmax,rmax;
  node(){
  val=lmax=rmax=-inf;
    l=r=sum=0;
  }
}tree[N<<2];
int a[N];

void push_up(int x){

  tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum;
  tree[x].lmax=max(tree[x<<1].lmax,tree[x<<1].sum+tree[x<<1|1].lmax);
  tree[x].rmax=max(tree[x<<1|1].rmax,tree[x<<1|1].sum+tree[x<<1].rmax);
  tree[x].val=max(max(tree[x<<1].val,tree[x<<1|1].val),tree[x<<1].rmax+tree[x<<1|1].lmax);

}


void build(int x,int l,int r){
  // cout<<x<<endl;
  tree[x].l=l;tree[x].r=r;
  
  if(l==r){
    tree[x].sum=tree[x].lmax=tree[x].rmax=tree[x].val=a[l];
    return ;
  }

  int mid=(l+r)>>1;
  build(x<<1,l,mid);
  build(x<<1|1,mid+1,r);
  push_up(x);

}

void modify(int x,int rt){

    if(tree[rt].l==tree[rt].r){

    tree[rt].sum=tree[rt].lmax=tree[rt].rmax=tree[rt].val=a[x];
    return ;

    }

    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(x<=mid)modify(x,rt<<1);
    else modify(x,rt<<1|1);
    push_up(rt);

}


node query(int a,int b,int rt){

  if(tree[rt].l>=a&&tree[rt].r<=b){
    return tree[rt];
  }
  int mid=(tree[rt].l+tree[rt].r)>>1;
  node lnode,rnode,ret;
  
  if(a<=mid){
    lnode=query(a,b,rt<<1);

  }
  if(b>mid){
    rnode=query(a,b,rt<<1|1);
  }

  ret.sum=lnode.sum+rnode.sum;
  ret.lmax=max(lnode.lmax,lnode.sum+rnode.lmax);
  ret.rmax=max(rnode.rmax,rnode.sum+lnode.rmax);
  ret.val=max(max(lnode.val,rnode.val),lnode.rmax+rnode.lmax);  

  return ret;

}

int main(){ 
  
  int n,m;cin>>n>>m;

  for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  
  build(1,1,n);

  while(m--){
    int k,x,y;
    scanf("%d %d %d",&k,&x,&y);

    if(k==1){

      if(x>y)swap(x,y);
      printf("%d
",query(x,y,1).val);

    }

    else {

      a[x]=y;
      modify(x,1);

    }


  }
  // system("pause");

  return 0;
}
View Code

Can you answer these queries III

实现区间加,询问区间gcd。

有区间加的形式,无法直接维护区间gcd。

考虑辗转相除法,gcd($x$,$y$,$z$)=gcd($x$,$y-x$,$z-y$)

对于区间加:让差分数组$b_l$+$d$,$b_{r+1}$-$d$

对于区间gcd,gcd[l,r]=gcd($a_l$,gcd[ b [l+1,r] ] )

用一个线段树维护差分数组,支持区间gcd和单点修改

考虑数组a动态变化,需要支持区间加和单点查询,用一个树状数组模拟,

在维护差分数组的区间gcd会出现负数的情况,取绝对值即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long 
const int N=6e5+500;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int a[N],b[N];
struct node{
  int g,l,r;
}tree[N<<4];

void push_up(int x){
  tree[x].g=gcd(tree[x<<1].g,tree[x<<1|1].g);
}

void build(int x,int l,int r){
  tree[x].l=l;tree[x].r=r;
  if(l==r){
    tree[x].g=b[l];
    return ;
  }
  int mid=(l+r)>>1;
  build(x<<1,l,mid);
  build(x<<1|1,mid+1,r);
  push_up(x);
}

void modify(int rt,int x,int d){

  if(tree[rt].l==x&&tree[rt].r==x){
    tree[rt].g+=d;
    return ;
  }
  
  int mid=(tree[rt].l+tree[rt].r)>>1;
  if(x<=mid)modify(rt<<1,x,d);
  else if(x>mid)modify(rt<<1|1,x,d);
  push_up(rt);

}

int query(int x,int l,int r){
  
  if(tree[x].l>=l&&tree[x].r<=r){
    return tree[x].g;
  }

  int mid=(tree[x].l+tree[x].r)>>1;
  if(r<=mid)return query(x<<1,l,r);
  if(l>mid)return query(x<<1|1,l,r);
  return abs( gcd(query(x<<1,l,r),query(x<<1|1,l,r)) );

}

int T[N];
#define lowbit(x)(x&-x)
int n,m;
void add(int x,int d){
  for(;x<=n;x+=lowbit(x))T[x]+=d;
}
int Sum(int x){
  int sum=0;
  for(;x;x-=lowbit(x))sum+=T[x];
  return sum;
}

signed main(){

  scanf("%lld %ld",&n,&m);
  // cin>>n>>m;
  
  for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
  b[1]=a[1];                
  add(1,b[1]);

  for(int i=2;i<=n;i++)b[i]=a[i]-a[i-1],add(i,b[i]);

  build(1,1,n);
  
  while(m--){
    char op;int x,y,z;
    getchar();
    scanf("%c",&op);
    // cout<<"test :"<<op<<endl;
    if(op=='C'){
      scanf("%lld %lld %lld",&x,&y,&z);
      modify(1,x,z);add(x,z);
      if(y+1<=n)modify(1,y+1,-z),add(y+1,-z);
    }

    else {
      scanf("%lld %lld",&x,&y);
      // cout<<"ans :";
      if(x==y)printf("%lld
",Sum(x));
      else printf("%lld
",gcd( Sum(x),query(1,x+1,y) ) );
    }

  }

  // system("pause");
  return 0;
}
View Code

楼兰图腾

树状数组维护逆序对即可。

#include<bits/stdc++.h>
using namespace std;
const int N=8e5+500;
#define lowbit(x)(x&-x)
typedef long long ll;
ll tree[N],y[N],l[N],r[N];
int n;
void init(){
    memset(tree,0,sizeof tree);
    // memset(l,0,sizeof l);
    // memset(r,0,sizeof r);
}

void add(int x,ll d){
    for(;x<=n;x+=lowbit(x))tree[x]+=d;
}

ll ask(int x){
    ll sum=0;
    for(;x;x-=lowbit(x))sum+=tree[x];
    return sum;
}

int  main(){
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&y[i]);
        ll ans1=0,ans2=0;
    init();
    
    for(int i=1;i<=n;i++){
        l[i]=max(0ll,(i-1)-ask(y[i]));
        add(y[i],1);
    }

    init();
    for(int i=n;i>=1;i--){
        r[i]=max(0ll,(n-i)-ask(y[i]));
        add(y[i],1);
    }
    for(int i=1;i<=n;i++)ans1+=l[i]*r[i];

    init();
    
    for(int i=1;i<=n;i++){
        l[i]=ask(y[i]-1);
        add(y[i],1);
    }

    init();
    
    for(int i=n;i>=1;i--){
        r[i]=ask(y[i]-1);
        add(y[i],1);
    }

    for(int i=1;i<=n;i++)ans2+=l[i]*r[i];
        cout<<ans1<<" "<<ans2<<endl;

    // system("pause");
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/littlerita/p/13906642.html