【洛谷P2839】【BZOJ2653】middle【主席树】
题目大意:
题目链接:
洛谷:https://www.luogu.org/problem/P2839
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=2653
一个长度为的序列,设其排过序之后为,其中位数定义为,其中从开始标号,除法取下整。给你一个长度为的序列。回答个这样的询问:的左端点在之间,右端点在之间的子序列中,最大的中位数。其中。位置也从开始标号。我会使用一些方式强制你在线。
思路:
注意本代码在洛谷没有A。
我们考虑套路性的二分它的中位数。然后把大于等于的数字标记为1,小于的数字标记为-1。这样如果我们可以在左端点在之间,右端点在之间的子序列中找出一个,使得它的和大于0,那么最终的最大中位数就在区间中,否则就在中。
我们要求是否有一个满足要求的区间使得它的和大于等于0,那么我们就对于每一个建立一棵权值线段树,维护每一个区间的和,最大前缀子段和,最大后缀子段和。
那么对于所有左端点在之间,右端点在之间的子序列,我们就可以分为三部分来计算:。
将区间的和,区间的最大后缀子段和,区间的最大前缀子段和求出来相加就是答案。
但是这样要对每一个建立一棵线段树,空间复杂度为。
发现对于,只有数字为的会改变,所以主席树就可以了。
时间复杂度。
代码:
#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;
}