【主席树】2012集训队互测 Middle

JZOJ 2902 集训队互测2012 middle(陈立杰)


【题目大意】给定一个序列,每次询问左端点在[a,b]中,右端点在[c,d]中的子序列中,最大的中位数。强制在线。


【题解】

这题虽然不是这几天做的,但是最近在搞数据结构,再总结一下还是会有收获的。(复习时应该看看)


设一个序列S从大到小排序后为S[1..k],则中位数为M=S[k/2向上取整]。

那就意味着,在序列S中,大于等于M的数大于等于k/2。如果把序列S转化为T,T[i]=1(S[i]>=M),-1(S[i]<M),则ΣT[i]一定非负。


这样我们很容易想到一个算法:二分答案+主席树

1.先按位置建一棵叶子节点权值为1的原始线段树(第一个版本的线段树)。要维护区间和sum,区间左侧最大前缀和lsum,和右侧最大前缀和rsum。

2.然后按权值从小到大的顺序依次在相应的位置修改,每次新建一个版本的线段树(可持久化)

假设当前到第i个版本的线段树,则要把第i-1大的数的位置权值修改为-1。这样在第i个版本的线段树上,比第i大的数还大(或等于)的数的位置上值为1,否则为-1。

3.二分答案。

二分到k时,就在第k个版本的线段树上查询。(程序实现时root[0]为第一个版本线段树的根)。

若Query(root[k-1],1,N,ask[1],ask[2]).rsum + Query(root[k-1],1,N,ask[2]+1,ask[3]-1).sum + Query(root[k-1],1,N,ask[3],ask[4]).lsum >= 0,就说明k可以为中位数,继续二分。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define fo(i,a,b) for (int i = a;i <= b;i ++)
#define fd(i,a,b) for (int i = a;i >= b;i --)

using namespace std;

const int maxn = 2000000;

struct node
{
	int lsum,rsum,sum,l,r;
	node(){}
	node(int a,int b,int c) {lsum = a,rsum = b,sum = c,l = r = 0;}
};
int root[maxn];
node tree[maxn];
int a[maxn],b[maxn],pos[maxn];
int N,Q,TOT,n,Last;
int ask[10];

bool cmp(int x,int y) { return a[x] < a[y]; }

void Readin()
{
	scanf("%d",&N);
	fo(i,1,N) scanf("%d",&a[i]),pos[i] = i;
}

void Discretize(){ sort(pos+1,pos+N+1,cmp); }

void Update(int z)
{
	int lson = tree[z].l;
	int rson = tree[z].r;
	tree[z].lsum = max(tree[lson].sum + tree[rson].lsum,tree[lson].lsum);
	tree[z].rsum = max(tree[rson].sum + tree[lson].rsum,tree[rson].rsum);
	tree[z].sum = tree[lson].sum + tree[rson].sum;
}

void AddPoint(int &z,int x,int l,int r,int p)
{
	z = ++ TOT;
	if (l == r) 
	{
		tree[z].l = tree[z].r = 0;
		tree[z].lsum = tree[z].rsum = -1;
		tree[z].sum = -1;
	} else 
	{
		int Mid = (l + r) >> 1;
		if (p <= Mid) 
		{
			AddPoint(tree[z].l,tree[x].l,l,Mid,p);
			tree[z].r = tree[x].r;
		} else 
		{
			AddPoint(tree[z].r,tree[x].r,Mid+1,r,p);
			tree[z].l = tree[x].l;
		}
		Update(z);
	}
}

void Filltree(int &z,int l,int r)
{
	z = ++ TOT;
	if (l == r)
	{
		tree[z].l = tree[z].r = 0;
		tree[z].lsum = tree[z].rsum = 1;
		tree[z].sum = 1;
	} else 
	{
		int Mid = (l + r) >> 1;
		Filltree(tree[z].l,l,Mid);
		Filltree(tree[z].r,Mid+1,r);
		Update(z);
	}
}

void Maketree()
{
	TOT = 0;
	Filltree(root[0],1,N);
	fo(i,1,N-1) 
		AddPoint(root[i],root[i-1],1,N,pos[i]);
}

void Initialize()
{
	Readin();
	Discretize();
	Maketree();
}

node Query(int z,int l,int r,int x,int y)
{
	if (y < x) return node(0,0,0);
	if (l == x && r == y) return tree[z];
	int Mid = (l + r) >> 1;
	if (y <= Mid) return Query(tree[z].l,l,Mid,x,y);
	else if (x > Mid) return Query(tree[z].r,Mid+1,r,x,y);
	else 
	{
		node ret,retl,retr;
		retl = Query(tree[z].l,l,Mid,x,Mid);
		retr = Query(tree[z].r,Mid+1,r,Mid+1,y);
		ret.lsum = max(retl.sum + retr.lsum,retl.lsum);
		ret.rsum = max(retr.sum + retl.rsum,retr.rsum);
		ret.sum = retl.sum + retr.sum;
		return ret;
	}
}

bool Check(int k)
{
	return Query(root[k-1],1,N,ask[1],ask[2]).rsum + Query(root[k-1],1,N,ask[2]+1,ask[3]-1).sum + Query(root[k-1],1,N,ask[3],ask[4]).lsum >= 0;
}

void Work()
{
	int L,R,Mid;
	L = 1;
	R = N;
	while (L < R)
	{
		Mid = (L + R + 1) >> 1;
		if (Check(Mid)) L = Mid;
		else R = Mid - 1;
	}
	Last = a[pos[L]];
	printf("%d\n",a[pos[L]]);
}
int main(void)
{
	Initialize();
	scanf("%d",&Q);
	Last = 0;
	while (Q)
	{
		fo(i,1,4) scanf("%d",&ask[i]);
		fo(i,1,4) ask[i] = (ask[i] + Last) % N + 1;
		sort(ask+1,ask+5);
		Work();
		Q --;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值