【洛谷P2839】【BZOJ2653】middle【主席树】

题目大意:

题目链接:
洛谷:https://www.luogu.org/problem/P2839
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=2653
一个长度为nn的序列aa,设其排过序之后为bb,其中位数定义为b[n2]b[\frac{n}{2}],其中a,ba,b00开始标号,除法取下整。给你一个长度为nn的序列ss。回答QQ个这样的询问:ss的左端点在[a,b][a,b]之间,右端点在[c,d][c,d]之间的子序列中,最大的中位数。其中a<b<c<da<b<c<d。位置也从00开始标号。我会使用一些方式强制你在线。


思路:

注意本代码在洛谷没有A。
我们考虑套路性的二分它的中位数。然后把大于等于midmid的数字标记为1,小于midmid的数字标记为-1。这样如果我们可以在左端点在[a,b][a,b]之间,右端点在[c,d][c,d]之间的子序列中找出一个,使得它的和大于0,那么最终的最大中位数就在区间[mid,maxn][mid,maxn]中,否则就在[0,mid1][0,mid-1]中。
我们要求是否有一个满足要求的区间使得它的和大于等于0,那么我们就对于每一个midmid建立一棵权值线段树,维护每一个区间[l,r][l,r]的和,最大前缀子段和,最大后缀子段和。
那么对于所有左端点在[a,b][a,b]之间,右端点在[c,d][c,d]之间的子序列,我们就可以分为三部分来计算:[a,b1]+[b,c]+[c+1,d][a,b-1]+[b,c]+[c+1,d]
将区间[b,c][b,c]的和,区间[a,b1][a,b-1]的最大后缀子段和,区间[c+1,d][c+1,d]的最大前缀子段和求出来相加就是答案。
但是这样要对每一个midmid建立一棵线段树,空间复杂度为O(n2logn)O(n^2\log n)
发现对于midmid+1mid\to mid+1,只有数字为midmid的会改变,所以主席树就可以了。
时间复杂度O(nlog2n)O(n\log^2 n)


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=50010;
int n,m,last,totel,a[N],b[N],root[N],head[N],q[5];

struct edge
{
    int next,x;
}e[N];

struct Treenode
{
    int lc,rc,sum,lmax,rmax;
};

struct Tree
{
    Treenode tree[N*40];
    int tot;
    
    int pushup(int x)
    {
        tree[x].rmax=max(tree[tree[x].rc].rmax,tree[tree[x].rc].sum+tree[tree[x].lc].rmax);
        tree[x].lmax=max(tree[tree[x].lc].lmax,tree[tree[x].lc].sum+tree[tree[x].rc].lmax);
        tree[x].sum=tree[tree[x].lc].sum+tree[tree[x].rc].sum;
    }
    
    void build(int &x,int l,int r)
    {
        x=++tot;
        tree[x].sum=tree[x].lmax=tree[x].rmax=r-l+1;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(tree[x].lc,l,mid);
        build(tree[x].rc,mid+1,r);
        pushup(x);
    }
    
    void insert(int now,int &x,int l,int r,int k)
    {
        if (!x)
        {
            x=++tot;
            tree[x]=tree[now];
        }
        if (l==r)
        {
            tree[x].sum=-1;
            tree[x].lmax=tree[x].rmax=0;
            return;
        }
        int mid=(l+r)>>1;
        if (k<=mid)
        {
            if (tree[x].lc==tree[now].lc) tree[x].lc=++tot,tree[tree[x].lc]=tree[tree[now].lc];
            insert(tree[now].lc,tree[x].lc,l,mid,k);
        }
        else
        {
            if (tree[x].rc==tree[now].rc) tree[x].rc=++tot,tree[tree[x].rc]=tree[tree[now].rc];
            insert(tree[now].rc,tree[x].rc,mid+1,r,k);
        } 
        pushup(x);
    }
    
    int ask_sum(int x,int l,int r,int ql,int qr)
    {
        if (l==ql && r==qr) return tree[x].sum;
        int mid=(l+r)>>1;
        if (qr<=mid) return ask_sum(tree[x].lc,l,mid,ql,qr);
        if (ql>mid) return ask_sum(tree[x].rc,mid+1,r,ql,qr);
        return ask_sum(tree[x].lc,l,mid,ql,mid)+ask_sum(tree[x].rc,mid+1,r,mid+1,qr);
    }
    
    int ask_lmax(int x,int l,int r,int ql,int qr)
    {
        if (l==ql && r==qr) return tree[x].lmax;
        int mid=(l+r)>>1;
        if (qr<=mid) return ask_lmax(tree[x].lc,l,mid,ql,qr);
        if (ql>mid) return ask_lmax(tree[x].rc,mid+1,r,ql,qr);
        int s1=ask_lmax(tree[x].lc,l,mid,ql,mid);
        int s2=ask_sum(tree[x].lc,l,mid,ql,mid)+ask_lmax(tree[x].rc,mid+1,r,mid+1,qr);
        return max(s1,s2);
    }
    
    int ask_rmax(int x,int l,int r,int ql,int qr)
    {
        if (l==ql && r==qr) return tree[x].rmax;
        int mid=(l+r)>>1;
        if (qr<=mid) return ask_rmax(tree[x].lc,l,mid,ql,qr);
        if (ql>mid) return ask_rmax(tree[x].rc,mid+1,r,ql,qr);
        int s1=ask_rmax(tree[x].rc,mid+1,r,mid+1,qr);
        int s2=ask_sum(tree[x].rc,mid+1,r,mid+1,qr)+ask_rmax(tree[x].lc,l,mid,ql,mid);
        return max(s1,s2);
    }
}Tree;

int binary()
{
    int l=1,r=totel,mid,ans;
    while (l<=r)
    {
        mid=(l+r)>>1;
        ans=Tree.ask_sum(root[mid],1,totel,q[2],q[3]);
        if (q[1]!=q[2]) ans+=Tree.ask_rmax(root[mid],1,totel,q[1],q[2]-1);
        if (q[3]!=q[4]) ans+=Tree.ask_lmax(root[mid],1,totel,q[3]+1,q[4]);
        if (ans>=0) l=mid+1;
            else r=mid-1;
    }
    return l-1;
}

int main()
{
    memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+n);
	totel=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+1+totel,a[i])-b;
		e[i].next=head[a[i]]; e[i].x=i;
		head[a[i]]=i;
	}
	Tree.build(root[1],1,totel);
	for (int i=2;i<=totel;i++)
	   for (int j=head[i-1];~j;j=e[j].next)
	       Tree.insert(root[i-1],root[i],1,totel,e[j].x);
	scanf("%d",&m);
	last=0;
	while (m--)
	{
	    scanf("%d%d%d%d",&q[1],&q[2],&q[3],&q[4]);
	    q[1]=(q[1]+last)%n+1; q[2]=(q[2]+last)%n+1;
	    q[3]=(q[3]+last)%n+1; q[4]=(q[4]+last)%n+1;
	    sort(q+1,q+5);
	    printf("%d\n",last=b[binary()]);
	}
	return 0;
}
posted @ 2019-09-15 14:10  全OI最菜  阅读(105)  评论(0编辑  收藏  举报