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

UPD on 2020.11.19

重构代码后在洛谷 AC 本题,请到 cnblos 上看 :https://www.cnblogs.com/stoorz/p/14007960.html


题目大意:

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


思路:

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


代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值