CF484E Sign on Fence (可持久化线段树+二分答案+区间合并)

题意:

给定一个长度为n的序列a[i]。给定m次询问。

每次查询为l,r,w,表示在区间l,r中,问对于连续的长为w的一段中最小的数字的最大值是多少。

题解:

先把所有元素按高度从大到小排序,以高度为时间轴,元素编号为下标建立可持久化线段树。

那么,第i棵线段树只会保存高度大于等于i的元素下标情况。

对于每个询问,二分答案,在当前二分答案的线段树中找区间l r内最长的连续线段,这里涉及区间合并,是之前从来没有接触过的知识点。

这道题无论是建树方法还是线段树信息的维护都是从没接触过的,需要好好复习。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
const int M=maxn*40;
int a[maxn],n,q,T[maxn];
int tot=1;
vector<pair<int,int> > z;
struct node {
    int l,r;
    int len,maxLen,pre,suf;
    //len表示每个节点代表的区间长度
    //maxLen表示每个节点内部最长的连续区间的长度
    //pre表示每个节点代表的区间的最大前缀
    //suf表示每个节点代表的区间的最大后缀 
}segTree[M];//可持久化线段树结构体 
bool cmp (pair<int,int> x,pair<int,int> y) {
    return x.first>y.first;//先按高度从大到小排序 
}
void push_up (int i) {
    if (segTree[segTree[i].l].len==segTree[segTree[i].l].pre) {
        segTree[i].pre=segTree[segTree[i].l].len+segTree[segTree[i].r].pre;
    }
    else {
        segTree[i].pre=segTree[segTree[i].l].pre;
    }
    
    if (segTree[segTree[i].r].len==segTree[segTree[i].r].suf) {
        segTree[i].suf=segTree[segTree[i].r].len+segTree[segTree[i].l].suf;
    }
    else {
        segTree[i].suf=segTree[segTree[i].r].suf;
    }
    
    segTree[i].maxLen=max(segTree[i].pre,segTree[i].suf);
    segTree[i].maxLen=max(segTree[i].maxLen,segTree[segTree[i].l].suf+segTree[segTree[i].r].pre);
    segTree[i].maxLen=max(segTree[i].maxLen,max(segTree[segTree[i].l].maxLen,segTree[segTree[i].r].maxLen));
} 

void up (int &root,int lst,int l,int r,int p) {
    root=tot++;
    segTree[root]=segTree[lst];
    segTree[root].len=r-l+1;
    segTree[lst].len=r-l+1;
    if (l==r) {
        segTree[root].maxLen=1;
        segTree[root].pre=segTree[root].suf=1;
        return;
    }
    int mid=(l+r)>>1;
    if (p<=mid) 
        up(segTree[root].l,segTree[lst].l,l,mid,p);
    else
        up(segTree[root].r,segTree[lst].r,mid+1,r,p);
    push_up(root);
}

node merge (node x,node y) {
    node ans;
    if (x.len==0) return y;
    if (x.len==x.pre) {
        ans.pre=x.len+y.pre;
    }
    else {
        ans.pre=x.pre;
    }
    
    if (y.suf==y.len) {
        ans.suf=y.len+x.suf;
    }
    else {
        ans.suf=y.suf;
    }
    
    ans.len=x.len+y.len;
    ans.maxLen=max(ans.suf,ans.pre);
    ans.maxLen=max(ans.maxLen,x.suf+y.pre);
    ans.maxLen=max(ans.maxLen,max(x.maxLen,y.maxLen));
    return ans;
}

node query (int root,int l,int r,int L,int R) {
    if (l>=L&&r<=R) {
        return segTree[root];
    }
    node ans;
    ans.l=ans.len=ans.pre=ans.suf=ans.r=ans.maxLen=0;
    int mid=(l+r)>>1;
    if (L<=mid) ans=merge(ans,query(segTree[root].l,l,mid,L,R));
    if (R>mid) ans=merge(ans,query(segTree[root].r,mid+1,r,L,R));
    return ans;
}

int main () {
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        scanf("%d",a+i);
        z.push_back(make_pair(a[i],i));
    }
    sort(z.begin(),z.end(),cmp);
    for (int i=0;i<n;i++) up(T[i+1],T[i],1,n,z[i].second);//以高度为轴,编号为下标建立线段树 
    scanf("%d",&q);
    while (q--) {
        int ql,qr,w;
        scanf("%d%d%d",&ql,&qr,&w);
        int l=1,r=n,ans=0;
        while (l<=r) {
            int mid=(l+r)>>1;
            if (query(T[mid],1,n,ql,qr).maxLen>=w) {
                ans=mid;
                r=mid-1;
            }
            else {
                l=mid+1;
            }
        }
        //printf("%d
",ans);
        printf("%d
",z[ans-1].first);
    }
}