zoukankan      html  css  js  c++  java
  • 解题报告 P2839 [国家集训队]middle

    P2839 [国家集训队]middle

    绝世好题

    首先对于求区间 ([l,r]) 的中位数,有一个套路可以套:

    二分一个值 (d) ,每次将区间内 (<d) 的点设为 (-1) ,将 (ge d) 的点设为 (1)。当区间和 (ge 0)(d) 值过大或刚好,若 (<0)(d) 值过小。

    我们继续观察题目。

    我们发现,每次询问的区间不固定,但是 ([b+1,c-1]) 这个区间是必须选择的,所以我们可以先求这个区间的和。

    由于我们要最大化中位数,所以我们可以在 ([a,b]) 中选取最大后缀,在 ([c,d]) 中选取最大前缀。

    将三个区间的和加起来,就是我们要求的和了。

    如果我们暴力统计查询,单次查询 (O(nlog n)) 显然过不了,所以要想办法优化。

    我们可以对每一个 (d) 建立一棵线段树同时维护区间和、最大前缀和、最大后缀和,下标维护元素位置。将 (<d) 的点设为 (-1) ,将 (ge d) 的点设为 (1),就可以区间查询了。

    但是这样干空间会炸,我们还要另想办法。

    将序列元素排序我们发现,对两个相邻的 (d) 值,其构造出来的 " (1,-1) " 序列只有一处不同。换句话说,它们的线段树只有一条路径不同。

    我们可以考虑使用主席树维护,将 (d) 所在位置的值改成 (-1) 即可,版本号依据 (d) 确定。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2e5+10;
    
    struct node
    {
    	int lson,rson;
    	int sum,lmax,rmax;//和,前缀,后缀
    } tree[N*60];
    
    int root[N],tot=0,q[10];
    
    int n,MAX;
    int arr[N],v[N];
    vector<int> pos[N];
    
    #define lnode tree[node].lson
    #define rnode tree[node].rson
    #define DEFMID int mid=(start+end)>>1
    
    void push_up(int node)
    {
    	tree[node].sum=tree[lnode].sum+tree[rnode].sum;//和
    	tree[node].lmax=max(tree[lnode].lmax,tree[lnode].sum+tree[rnode].lmax);//前缀和
    	tree[node].rmax=max(tree[rnode].rmax,tree[rnode].sum+tree[lnode].rmax);//后缀和
    }
    
    int build(int start,int end)
    {
    	int node=++tot;
    	if(start==end)
    	{
    		tree[node].sum=tree[node].lmax=tree[node].rmax=1;
    		return node;
    	}
    	DEFMID;
    	lnode=build(start,mid);
    	rnode=build(mid+1,end);
    	push_up(node);
    	return node;
    }
    
    #define lnode1 tree[node1].lson
    #define rnode1 tree[node1].rson
    
    int update(int node,int start,int end,int val)
    {
    	int node1=++tot;
    	tree[node1]=tree[node];
    	if(start==end)
    	{
    		tree[node1].lmax=tree[node1].rmax=tree[node1].sum=-1;
    		return node1;
    	}
    	DEFMID;
    	if(val<=mid) lnode1=update(lnode,start,mid,val);
    	else rnode1=update(rnode,mid+1,end,val);
    	push_up(node1);
    	return node1;
    }
    
    int query1(int node,int start,int end,int l,int r)
    {
    	if(r<l) return 0;
    	if(l<=start&&end<=r) return tree[node].sum;
    	DEFMID;
    	if(r<=mid) return query1(lnode,start,mid,l,r);
    	else if(l>mid) return query1(rnode,mid+1,end,l,r);
    	else return query1(lnode,start,mid,l,r)+query1(rnode,mid+1,end,l,r);
    }
    
    int query2(int node,int start,int end,int l,int r)//最大前缀和;
    {
    	if(l<=start&&end<=r) return tree[node].lmax;
    	DEFMID;
    	if(r<=mid) return query2(lnode,start,mid,l,r);
    	else if(l>mid) return query2(rnode,mid+1,end,l,r);
    	else
    	{
    		int t1=query2(lnode,start,mid,l,mid);
    		int t2=query1(lnode,start,mid,l,mid)+query2(rnode,mid+1,end,mid+1,r);
    		return max(t1,t2);
    	}
    }
    
    int query3(int node,int start,int end,int l,int r)
    {
    	if(l<=start&&end<=r) return tree[node].rmax;
    	DEFMID;
    	if(r<=mid) return query3(lnode,start,mid,l,r);
    	else if(l>mid) return query3(rnode,mid+1,end,l,r);
    	else
    	{
    		int t1=query3(rnode,mid+1,end,mid+1,r);
    		int t2=query1(rnode,mid+1,end,mid+1,r)+query3(lnode,start,mid,l,mid);
    		return max(t2,t1);
    	}
    }
    
    bool check(int mid)
    {
    	int a=query1(root[mid],1,n,q[2]+1,q[3]-1);//求和
    	int c=query2(root[mid],1,n,q[3],q[4]);//最大前缀和
    	int b=query3(root[mid],1,n,q[1],q[2]);//最大后缀和
    	return a+b+c>=0;
    }
    
    int solve()
    {
    	int l=1,r=MAX;
    	while(l<r)
    	{
    		int mid=(l+r+1)>>1;
    		if(check(mid)) l=mid;
    		else r=mid-1;
    	}
    	return l;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	root[0]=build(1,n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&arr[i]);
    		v[i]=arr[i];
    	}
    	sort(v+1,v+1+n);
    	MAX=unique(v+1,v+1+n)-v-1;
    	for(int i=1;i<=n;i++)
    	{
    		arr[i]=lower_bound(v+1,v+1+n,arr[i])-v;
    		pos[arr[i]].push_back(i);
    	}
    	for(int i=1;i<=MAX;i++)//构建主席树
    	{
    		root[i]=root[i-1];
    		for(int j=0;j<pos[i-1].size();j++)
    			root[i]=update(root[i],1,n,pos[i-1][j]);//插入
    	}
    	int m,ans=0;
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++)
    	{
    		for(int t=1;t<=4;t++) scanf("%d",q+t);
    		for(int t=1;t<=4;t++) q[t]=(q[t]+ans)%n+1;//我们的下标从1开始
    		sort(q+1,q+5);
    		ans=v[solve()];
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    centos 安装docker-ce
    quartz.net .netcore 定时任务部署到linux
    c# 获取linux 磁盘信息
    nginx 安装
    async await 理解
    Remote side unexpectedly closed network connection
    centos 安装。net
    yum 操作
    centos7下安装mysql5.7
    git 本地仓库版本无法从远程更新到本地
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/14454757.html
Copyright © 2011-2022 走看看