0
点赞
收藏
分享

微信扫一扫

Couriers 可持久化线段树

哈哈我是你爹呀 2022-03-30 阅读 74
算法

题意:

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

思路:

住席树的板子题,常见的板子题为求[l,r]区间内第k大的数,这里是让求是否存在一个数的出现次数严格大于区间内其他出现的数,其实是一样的,令k=(r-l+1)/2,一个区间分成两半,肯定最多只有一个的区间存在sum值大于等于k,如果一直遍历到某个叶子节点其sum>k,则说明该叶子节点对应的值就是[l,r]区间内满足条件的值,否则说明没有。

样例:

 代码:

#include <bits/stdc++.h>

using namespace std;

const int N=5e5+10;
const int M=20*N;
int n,m;
int root[N];
int lch[M],rch[M],sum[M],tot;
void cpy(int from,int to){
	lch[to]=lch[from];
	rch[to]=rch[from];
	sum[to]=sum[from]; 
}
void insert(int &u,int old,int L,int R,int x){
	u=++tot;
	cpy(old,u);
	sum[u]++;
	if(L==R)
		return ;
	int mid=(L+R)>>1;
	if(x<=mid)
		insert(lch[u],lch[u],L,mid,x);
	else
		insert(rch[u],rch[u],mid+1,R,x);
}
int query(int s,int t,int L,int R,int k){
	if(sum[t]-sum[s]<=k){
		return 0;
	}
	if(L==R){
		if(sum[t]-sum[s]>k)
			return  L;
		return 0; 
	}
	int lc=sum[lch[t]]-sum[lch[s]];
	int mid=(L+R)>>1;
	if(lc>k){
		return query(lch[s],lch[t],L,mid,k);
	}
	else
		return query(rch[s],rch[t],mid+1,R,k);
}
int main(){
	scanf("%d%d",&n,&m);
	int tt;
	for(int i=1;i<=n;i++){
		scanf("%d",&tt);
		insert(root[i],root[i-1],1,N-1,tt);
	}
	int l,r;
	while(m--){
		scanf("%d%d",&l,&r);
		int k=(r-l+1)/2;
		int res=query(root[l-1],root[r],1,N-1,k);
		printf("%d\n",res);
	}
}
举报

相关推荐

0 条评论