[bzoj十连测第三场 B]线段树

题目大意

有一个长度为n的序列与m个修改操作,每个修改操作是将序列[l,r]的元素都修改为这个区间的最大值。
现有q个操作,要么是修改序列的一个元素,要么是询问执行[l,r]的修改操作后,第k个元素是多少。询问之间独立,而修改会造成影响。

搞一搞

我们容易发现,每一个位置都可以被表示成一段区间的最大值。
我们枚举右端点r来离线做,把所有询问操作挂在其对应右端点上。
例如位置k,找到当前操作前最后一个覆盖其的操作,然后继续找最后一个覆盖该区间左端点的操作,最后一个覆盖该区间右端点的操作,最后一个覆盖最后一个覆盖该区间左端点的操作区间的左端点……
也就是,我们需要一直往左找,一直往右找,找到最左的ll与最右的rr,使得k这个位置可以表示成[ll,rr]的最大值。
把每个操作当做一个结点,我们维护两颗树:一棵叫左树一棵叫右树,左树中,一个结点的父亲所对应区间是在该结点之前的最后一个可以覆盖其左端点的区间,右树同理维护往右。倍增一下,便可以快速找到ll与rr。
如何得知最晚覆盖其的区间?可以用线段树,每加入一个区间就区间赋值。
然后,顺序扫q个操作,用线段树维护a,遇到修改就修改,遇到询问就询问对应区间最大值。

#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int left[maxn][25],right[maxn][25];
int a[maxn],tree[maxn*4],set[maxn*4],b[maxn][2];
bool bz[maxn*4];
struct dong{
    int l,r,k,ll,rr;
    bool p;
} ask[maxn];
int h[maxn],go[maxn],next[maxn];
int i,j,k,l,t,n,m,q,tot,top;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void mark(int p,int x){
    bz[p]=1;
    set[p]=x;
}
void down(int p){
    if (bz[p]){
        mark(p*2,set[p]);
        mark(p*2+1,set[p]);
        bz[p]=0;
    }
}
void modify(int p,int l,int r,int a,int b,int x){
    if (l==a&&r==b){
        mark(p,x);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) modify(p*2,l,mid,a,b,x);
    else if (a>mid) modify(p*2+1,mid+1,r,a,b,x);
    else{
        modify(p*2,l,mid,a,mid,x);
        modify(p*2+1,mid+1,r,mid+1,b,x);
    }
}
int get(int p,int l,int r,int a){
    if (l==r) return set[p];
    down(p);
    int mid=(l+r)/2;
    if (a<=mid) return get(p*2,l,mid,a);else get(p*2+1,mid+1,r,a);
}
void change(int p,int l,int r,int a,int b){
    if (l==r){
        tree[p]=b;
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) change(p*2,l,mid,a,b);else change(p*2+1,mid+1,r,a,b);
    tree[p]=max(tree[p*2],tree[p*2+1]);
}
int query(int p,int l,int r,int a,int b){
    if (l==a&&r==b) return tree[p];
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b);
    else return max(query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b));
}
int getleft(int x,int y){
    int j=floor(log(m)/log(2));
    while (j>=0){
        if (left[x][j]>=y) x=left[x][j];
        j--;
    }
    return x;
}
int getright(int x,int y){
    int j=floor(log(m)/log(2));
    while (j>=0){
        if (right[x][j]>=y) x=right[x][j];
        j--;
    }
    return x;
}
int main(){
    freopen("segment.in","r",stdin);freopen("segment.out","w",stdout);
    n=read();m=read();q=read();
    fo(i,1,n) a[i]=read();
    fo(i,1,m){
        b[i][0]=read();
        b[i][1]=read();
    }
    fo(i,1,q){
        t=read();
        ask[i].l=read();ask[i].r=read();
        if (t==2){
            ask[i].k=read();
            add(ask[i].r,i);
            ask[i].p=1;
        }
    }
    fo(i,1,m){
        left[i][0]=get(1,1,n,b[i][0]);
        right[i][0]=get(1,1,n,b[i][1]);
        modify(1,1,n,b[i][0],b[i][1],i);
        fo(j,1,floor(log(m)/log(2))){
            left[i][j]=left[left[i][j-1]][j-1];
            right[i][j]=right[right[i][j-1]][j-1];
        }
        t=h[i];
        while (t){
            j=get(1,1,n,ask[go[t]].k);
            if (j<ask[go[t]].l) ask[go[t]].ll=ask[go[t]].rr=ask[go[t]].k;
            else{
                ask[go[t]].ll=b[getleft(j,ask[go[t]].l)][0];
                ask[go[t]].rr=b[getright(j,ask[go[t]].l)][1];
            }
            t=next[t];
        }
    }
    fo(i,1,n) change(1,1,n,i,a[i]);
    fo(i,1,q)
        if (ask[i].p) printf("%d\n",query(1,1,n,ask[i].ll,ask[i].rr));
        else change(1,1,n,ask[i].l,ask[i].r);
}
    原文作者:B树
    原文地址: https://blog.csdn.net/werkeytom_ftd/article/details/51883863
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞