• 【洛谷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^2log n)
    发现对于midmid+1mid o mid+1,只有数字为midmid的会改变,所以主席树就可以了。
    时间复杂度O(nlog2n)O(nlog^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
    ",last=b[binary()]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    python之简单爬虫
    python之正则表达式
    python之面向对象
    python之模块与包
    python之循环嵌套与算法
    linux shell实现从函数返回数组
    linux脚本实现数组值相加
    linux中使用函数输出
    linux shelll中显示的意义
    lsof命令
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998028.html
Copyright © 2020-2023  润新知