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,mid−1]中。
我们要求是否有一个满足要求的区间使得它的和大于等于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,b−1]+[b,c]+[c+1,d]。
将区间
[
b
,
c
]
[b,c]
[b,c]的和,区间
[
a
,
b
−
1
]
[a,b-1]
[a,b−1]的最大后缀子段和,区间
[
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
mid→mid+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;
}