【BZOJ2653】middle,主席树(非权值线段树)维护序列和信息+二分答案

传送门
写在前面:虽然这是一道我再也不想写的题目,但很好很有价值
思路:
cxlove大神:

要求中位数最大,首先二分中位数,然后判断可行不可行。
判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1。这样的话,求一次最大区间和 如果大于等于0,则说明可行。

这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树
具体实现

对于题目要求,我们很容易维护[b+1,c-1]间的信息,即求得序列总和,而[a,b]的右起最大子序列和(必须包含右端点)与[c,d]左起最大子序列和(必须包含左端点)确实把我难了好久,唉……

一开始建树的时候每个位置都是1(原本所有位置的标记都是1)。序列按照从小到大顺序插入主席树。做第i棵树的时候,它第i-1大的数的位置上改为-1。什么意思呢,就是说,如果X取的是第i大的那个数,那么第i-1大的数的标记就要改为-1(更小的那些在做第i-1棵树的时候就已经改为-1了)。于是在询问X的时候是计算root[X]这棵线段树里面的各种值了。
最后是如何求[[a,b],[c,d]]区间的最大子段和。其实很简单,因为(b,c)这段是一定要取的,所以答案就是[a,b]的最大右连续和+(b,c)的区间和+[c,d]的最大左连续和。

注意:
离散化后的值,下标什么的好烦人啊!很容易搞混的说
代码:

#include<bits/stdc++.h>
#define M 20004
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
using namespace std;
int n,q,cnt,lastans;
int c[M],ID[M];
struct disc
{
    int data,id;
    bool operator <(const disc other)const
    {
        return data<other.data;
    }
}b[M];
struct Chairman_tree
{
    int sum_L,sum_R,sum,ch[2];
}a[M*16];
void pushup(int rt,int begin,int end)
{
    int mid=(begin+end)>>1;
    a[rt].sum=a[ls(rt)].sum+a[rs(rt)].sum;
    a[rt].sum_L=max(a[ls(rt)].sum_L,a[ls(rt)].sum+a[rs(rt)].sum_L);
    a[rt].sum_R=max(a[rs(rt)].sum_R,a[rs(rt)].sum+a[ls(rt)].sum_R);
}
void build(int rt,int begin,int end)
{
    if (begin==end) 
    {
        a[rt]=(Chairman_tree){1,1,1,{0,0}};
        return;
    }
    int mid=(begin+end)>>1;
    ls(rt)=++cnt;
    rs(rt)=++cnt;
    build(ls(rt),begin,mid);
    build(rs(rt),mid+1,end);
    pushup(rt,begin,end);
}
void insert(int now,int L,int R,int rt,int pos,int val)
{
    if (L==R)
    {
        a[rt]=(Chairman_tree){val,val,val,{0,0}};
        return;
    }
    int mid=(L+R)>>1;
    if (mid>=pos)
        rs(rt)=rs(now),
        ls(rt)=++cnt,
        insert(ls(now),L,mid,ls(rt),pos,val);
    else
        ls(rt)=ls(now),
        rs(rt)=++cnt,
        insert(rs(now),mid+1,R,rs(rt),pos,val);
    pushup(rt,L,R);
}
int get_sum(int rt,int begin,int end,int l,int r)
{
    if (l>r) return 0;
    if (l<=begin&&end<=r) return a[rt].sum;
    int mid=(begin+end)>>1,ans=0;
    if (mid>=l) ans+=get_sum(ls(rt),begin,mid,l,r);
    if (mid<r) ans+=get_sum(rs(rt),mid+1,end,l,r);
    return ans;
}
int get_L(int rt,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return a[rt].sum_L;
    int mid=(begin+end)>>1;
    if (mid>=r) return get_L(ls(rt),begin,mid,l,r);
    else if (mid<l) return get_L(rs(rt),mid+1,end,l,r);
    else return max(get_L(ls(rt),begin,mid,l,r),get_sum(ls(rt),begin,mid,l,r)+get_L(rs(rt),mid+1,end,l,r));
}
int get_R(int rt,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return a[rt].sum_R;
    int mid=(begin+end)>>1;
    if (mid>=r) return (0,get_R(ls(rt),begin,mid,l,r));
    else if (mid<l) return get_R(rs(rt),mid+1,end,l,r);
    else return max(get_R(rs(rt),mid+1,end,l,r),get_sum(rs(rt),mid+1,end,l,r)+get_R(ls(rt),begin,mid,l,r));
}
main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",c+i),
        b[i].id=i,b[i].data=c[i];
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++)
        c[b[i].id]=i,
        ID[i]=b[i].data;
    cnt=n;
    build(1,1,n);
    for (int i=2;i<=n;i++) insert(i-1,1,n,i,b[i-1].id,-1);
    scanf("%d",&q);
    int p[4];
    while (q--)
    {
        for (int i=0;i<4;i++)
            scanf("%d",p+i),
            p[i]=(p[i]+lastans)%n,
            p[i]++;
        sort(p,p+4);
        int l=1,r=n,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if (get_sum(mid,1,n,p[1]+1,p[2]-1)+get_R(mid,1,n,p[0],p[1])+get_L(mid,1,n,p[2],p[3])>=0)
                l=mid+1,lastans=ID[mid];
            else
                r=mid-1;
        }
        printf("%d\n",lastans);
    }
}

你可能感兴趣的:(【BZOJ2653】middle,主席树(非权值线段树)维护序列和信息+二分答案)