现在的位置: 首页 > 综合 > 正文

bzoj3524[Poi2014]Couriers

2018年01月13日 ⁄ 综合 ⁄ 共 1607字 ⁄ 字号 评论关闭

Description

给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。

Input

第一行两个数n,m。
第二行n个数,a[i]。
接下来m行,每行两个数l,r,表示询问[l,r]这个区间。

Output

m行,每行对应一个答案。

Sample Input

7 5

1 1 3 2 3 4 3

1 3

1 4

3 7

1 7

6 6

Sample Output

1

0

3

0

4

HINT

【数据范围】

n,m≤500000

第一次打可持久化线段树……

可持久化线段树是这样一个数据结构:它不仅实现线段树的操作,而且可以访问线段树的历史版本。

做法是“建n棵线段树”。

当然不是n^2logn的满二叉树啦

对于这题:首先搞出一颗权值线段树(就是区间(l,r)表示数字大小在(l,r)中的有多少个,a[i]<=10^9之类太大的要离散搞)。

每次增加一个数k进去的时候,相当于从根节点到叶节点的所有包含k的区间的sum+1。

但是我们要保留以前的版本,怎么做呢?

很容易发现每次加入一个k,修改的都只有树上的一条从根到叶的链上的sum,也就是说其他数据我们是可以共用的

所以每次加入k的时候只要再插入一条链就可以完成修改。

比如当前我们插入链做到一个点,首先把它的左右儿子改成它的上一个版本的左右儿子。然后看k在左边还是右边,再往那一边继续递归下去

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mx 10000010
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,treesize;
int root[500001];
int ls[mx],rs[mx],tot[mx];
inline void insert(int l,int r,int x,int &y,int dat)
{
	y=++treesize;
	tot[y]=tot[x]+1;
	if (l==r) return;
	ls[y]=ls[x];rs[y]=rs[x];
	int mid=(l+r)>>1;
	if (dat<=mid)insert(l,mid,ls[x],ls[y],dat);
	else insert(mid+1,r,rs[x],rs[y],dat);
}
inline int ask(int l,int r)
{
	int ll=1,rr=n,request=(r-l+1)>>1;
	int x=root[l-1],y=root[r];
	while (ll!=rr)
	{
		if (tot[y]-tot[x]<=request) return 0;
		int mid=(ll+rr)>>1;
		if (tot[ls[y]]-tot[ls[x]]>request)
		{rr=mid;x=ls[x];y=ls[y];}
		else if (tot[rs[y]]-tot[rs[x]]>request)
		{ll=mid+1;x=rs[x];y=rs[y];}
		else return 0;
	}
	return ll;
}
int main()
{
	n=read();m=read();
	for (int i=1;i<=n;i++)
	{
		int x=read();
		insert(1,n,root[i-1],root[i],x);
	}
	for (int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		printf("%d\n",ask(x,y));
	}
}

抱歉!评论已关闭.