1540-线段树区间合并操作

#include<stdio.h>
#include<iostream>
#include<stack>
#include<cstring>
using namespace std;
const int MAX=1000000;
struct pr {
    int lsum,rsum,msum;
    int left,right;                   
}tr[MAX+10];
int n,m;
inline int ll(int k) {return 2*k;}          
inline int rr(int k) {return 2*k+1;}         
inline int mid(int kk1,int kk2) {return (kk1+kk2)>>1;}


void pushup(int k){//m=r-l+1;
    int l=tr[k].left,r=tr[k].right;
    int mi=mid(l,r);
    tr[k].lsum =tr[ll(k)].lsum;
    tr[k].rsum =tr[rr(k)].rsum;
    if (tr[k].lsum == mi-l+1) tr[k].lsum+=tr[rr(k)].lsum;
    if (tr[k].rsum == r-mi) tr[k].rsum+=tr[ll(k)].rsum;
    tr[k].msum = max(tr[rr(k)].lsum+tr[ll(k)].rsum,max(tr[ll(k)].msum,tr[rr(k)].msum));
    return;
}


void build(int k,int s,int t) {
    tr[k].left=s;tr[k].right=t;
    if(s==t) {tr[k].rsum=tr[k].lsum=tr[k].msum=1;return;}               
    build(ll(k),s,mid(s,t));
    build(rr(k),mid(s,t)+1,t);
    pushup(k);        //pushup
}

void modify(int k,int in,int x) {
    int l=tr[k].left,r=tr[k].right;
    if(l==r) {
        if(x==1)
            tr[k].lsum=tr[k].msum=tr[k].rsum=0;
        else
            tr[k].lsum=tr[k].msum=tr[k].rsum=1;
        return ;
    }
    int mi=mid(l,r);
    if(in<=mi) modify(ll(k),in,x);
    else if(in>mi) modify(rr(k),in,x);
    pushup(k);
}

int query(int k,int x) {
    int l=tr[k].left,r=tr[k].right;
    if(l==r||tr[k].msum==0||tr[k].msum==r-l+1) return tr[k].msum;

    int mi=mid(l,r);
    int res=0;
    if (x<=mi){
        if(tr[ll(k)].right-tr[ll(k)].rsum+1<=x)
            res=query(ll(k),x)+query(rr(k),mi+1);
        else
            res=query(ll(k),x);
    }
    else if(x>mi){
        if(tr[rr(k)].lsum+tr[rr(k)].left-1>=x)
            res=query(rr(k),x)+query(ll(k),mi);
        else
            res=query(rr(k),x);
    }
    return res;
}


int main()
{
    while(cin>>n>>m){
        build(1,1,n);
        char s[10];
        int x;
        stack<int> Q;
        for(int i=0;i<m;i++){
            scanf("%s",s);
            if(s[0]=='D'){
                scanf("%d",&x);
                Q.push(x);
                modify(1,x,1);
            }
            else if(s[0]=='Q'){
                scanf("%d",&x);
                printf("%d
",query(1,x));
            }
            else{
                int y=Q.top();
                Q.pop();
                modify(1,y,0);
            }
        }

    }
    return 0;
}


这是一道基础的线段树合并基本操作。

题意就是单点修改,然后查询单点所在的连续合法区间,初始化都是合法的。

这题的考点应该就是查询部分。

查询部分单独拿出来说一下,

if (x<=mi){
        if(tr[ll(k)].right-tr[ll(k)].rsum+1<=x)
            res=query(ll(k),x)+query(rr(k),mi+1);
        else
            res=query(ll(k),x);
    }

就拿查询点在mid的左来举个栗子,考虑到在左面如果只查询左区间很可能得到错误答案(例如x在rsum里那么q(x)的答案就会漏掉r右边的部分),所以在另一个区间查他的左端点就可以啦,所以这题大致就是这样的了。。其他类比就行了。。

蒟蒻写的搓大家见谅。。。继续努力。。鲤鱼王加油!

原文地址:https://www.cnblogs.com/zhangxianlong/p/10672587.html