标签:
传送门
写在前面:虽然这是一道我再也不想写的题目,但很好很有价值
思路:
cxlove大神:
要求中位数最大,首先二分中位数,然后判断可行不可行。
判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1。这样的话,求一次最大区间和 如果大于等于0,则说明可行。
这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树
具体实现
对于题目要求,我们很容易维护[b+1,c-1]间的信息,即求得序列总和,而[a,b]的右起最大子序列和(必须包含右端点)与[c,d]左起最大子序列和(必须包含左端点)确实把我难了好久,唉……
一开始建树的时候每个位置都是1(原本所有位置的标记都是1)。序列按照从小到大顺序插入主席树。做第i棵树的时候,它第i-1大的数的位置上改为-1。什么意思呢,就是说,如果X取的是第i大的那个数,那么第i-1大的数的标记就要改为-1(更小的那些在做第i-1棵树的时候就已经改为-1了)。于是在询问X的时候是计算root[X]这棵线段树里面的各种值了。
最后是如何求[[a,b],[c,d]]区间的最大子段和。其实很简单,因为(b,c)这段是一定要取的,所以答案就是[a,b]的最大右连续和+(b,c)的区间和+[c,d]的最大左连续和。
注意:
离散化后的值,下标什么的好烦人啊!很容易搞混的说
代码:
#include<bits/stdc++.h>
#define M 20004
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
using namespace std;
int n,q,cnt,lastans;
int c[M],ID[M];
struct disc
{
int data,id;
bool operator <(const disc other)const
{
return data<other.data;
}
}b[M];
struct Chairman_tree
{
int sum_L,sum_R,sum,ch[2];
}a[M*16];
void pushup(int rt,int begin,int end)
{
int mid=(begin+end)>>1;
a[rt].sum=a[ls(rt)].sum+a[rs(rt)].sum;
a[rt].sum_L=max(a[ls(rt)].sum_L,a[ls(rt)].sum+a[rs(rt)].sum_L);
a[rt].sum_R=max(a[rs(rt)].sum_R,a[rs(rt)].sum+a[ls(rt)].sum_R);
}
void build(int rt,int begin,int end)
{
if (begin==end)
{
a[rt]=(Chairman_tree){1,1,1,{0,0}};
return;
}
int mid=(begin+end)>>1;
ls(rt)=++cnt;
rs(rt)=++cnt;
build(ls(rt),begin,mid);
build(rs(rt),mid+1,end);
pushup(rt,begin,end);
}
void insert(int now,int L,int R,int rt,int pos,int val)
{
if (L==R)
{
a[rt]=(Chairman_tree){val,val,val,{0,0}};
return;
}
int mid=(L+R)>>1;
if (mid>=pos)
rs(rt)=rs(now),
ls(rt)=++cnt,
insert(ls(now),L,mid,ls(rt),pos,val);
else
ls(rt)=ls(now),
rs(rt)=++cnt,
insert(rs(now),mid+1,R,rs(rt),pos,val);
pushup(rt,L,R);
}
int get_sum(int rt,int begin,int end,int l,int r)
{
if (l>r) return 0;
if (l<=begin&&end<=r) return a[rt].sum;
int mid=(begin+end)>>1,ans=0;
if (mid>=l) ans+=get_sum(ls(rt),begin,mid,l,r);
if (mid<r) ans+=get_sum(rs(rt),mid+1,end,l,r);
return ans;
}
int get_L(int rt,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return a[rt].sum_L;
int mid=(begin+end)>>1;
if (mid>=r) return get_L(ls(rt),begin,mid,l,r);
else if (mid<l) return get_L(rs(rt),mid+1,end,l,r);
else return max(get_L(ls(rt),begin,mid,l,r),get_sum(ls(rt),begin,mid,l,r)+get_L(rs(rt),mid+1,end,l,r));
}
int get_R(int rt,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return a[rt].sum_R;
int mid=(begin+end)>>1;
if (mid>=r) return (0,get_R(ls(rt),begin,mid,l,r));
else if (mid<l) return get_R(rs(rt),mid+1,end,l,r);
else return max(get_R(rs(rt),mid+1,end,l,r),get_sum(rs(rt),mid+1,end,l,r)+get_R(ls(rt),begin,mid,l,r));
}
main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",c+i),
b[i].id=i,b[i].data=c[i];
sort(b+1,b+n+1);
for (int i=1;i<=n;i++)
c[b[i].id]=i,
ID[i]=b[i].data;
cnt=n;
build(1,1,n);
for (int i=2;i<=n;i++) insert(i-1,1,n,i,b[i-1].id,-1);
scanf("%d",&q);
int p[4];
while (q--)
{
for (int i=0;i<4;i++)
scanf("%d",p+i),
p[i]=(p[i]+lastans)%n,
p[i]++;
sort(p,p+4);
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if (get_sum(mid,1,n,p[1]+1,p[2]-1)+get_R(mid,1,n,p[0],p[1])+get_L(mid,1,n,p[2],p[3])>=0)
l=mid+1,lastans=ID[mid];
else
r=mid-1;
}
printf("%d\n",lastans);
}
}
【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案
标签:
原文地址:http://blog.csdn.net/xym_csdn/article/details/51335041