[FROM WOJ]#2614[Poi2014]Couriers(看主席树模板请进)

#2614[Poi2014]Couriers

主席树模板题

题面
给一个长度为n的序列a。1≤a[i]≤n。
【数据范围】
n,m≤500000

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

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

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

样例输入
7 5
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6

样例输出
1
0
3
0
4

SOL
很容易想到用主席树来切这道题
题目的意思有点像找第k大数,与woj #1903略有区别的是,她要求你找出区间[L,R]中出现次数超过(r-l+1)/2的数——其实她的要求也就是让你找到[L,R]中的一个出现次数超过len(l,r)/2的数,那么也就意味着,你最多在这个区间找到一个合法的数。
这听起来像是一句废话,但是这句话让这道题变得很简单——就是找第k大,只不过k=len(l,r)/2+1而已。

代码:

#include<bits/stdc++.h>
#define limit ((v-u+1)>>1) 
#define N 500505
int n,m;
using namespace std;
inline int rd(){
	int w=1,data=0;static char ch='0';
	ch=getchar();
	while((ch!='-')&&(!isdigit(ch)))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
	return data*w;
}
inline void write(int x){
	if(x<0){putchar('-');x=-x;}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
int tot,a[N],h[N],rt[N<<5];
struct tree{
	int l,r,sum;
}t[N<<5];
inline int build(int l,int r){
	++tot;int root=tot;t[root].sum=0;
	if(l<r){
		int mid=l+r>>1;
		t[root].l=build(l,mid);
		t[root].r=build(mid+1,r);
	}
	return root;
}
inline int update(int p,int ql,int qr,int pre){
	++tot;int root=tot;
	t[root].l=t[pre].l,t[root].r=t[pre].r,t[root].sum=t[pre].sum+1;
	if(ql==qr)return root;
	int mid=ql+qr>>1;
	if(p<=mid)t[root].l=update(p,ql,mid,t[pre].l);
	else t[root].r=update(p,mid+1,qr,t[pre].r); 
	return root;
}
inline int query(int ql,int qr,int u,int v,int k){
	if(ql==qr)return ql;
	int mid=ql+qr>>1;
	int num1=t[t[v].l].sum-t[t[u].l].sum,num2=t[t[v].r].sum-t[t[u].r].sum;
	if(num1>k)return query(ql,mid,t[u].l,t[v].l,k);
	else if(num2>k)return query(mid+1,qr,t[u].r,t[v].r,k);else return 0;
}
int main(){
	n=rd();m=rd();
	for(int register i=1;i<=n;i++){
		a[i]=rd();
		h[i]=a[i];
	}
	sort(h+1,h+n+1);
	int nn=unique(h+1,h+n+1)-h-1;
	rt[0]=build(1,nn);
	for(int register i=1;i<=n;i++){
		int pos=lower_bound(h+1,h+nn+1,a[i])-h;
		rt[i]=update(pos,1,nn,rt[i-1]);
	}h[0]=0;
	while(m--){
		int u=rd(),v=rd();int ans=query(1,nn,rt[u-1],rt[v],limit);
		write(h[ans]);putchar('\n');
	}return 0;
}
//码风略丑,还望神仙们见谅!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值