【主席树】【Luogu P3567】KUR-Couriers

这篇博客介绍了一种利用树状数组(也称作线段树)解决区间查询问题的方法,具体为:给定一个数列,在每次查询时判断区间内是否存在一个数出现次数超过区间长度的一半。博主提供了思路分析、代码模板和实际代码实现,并通过LuoguP3567题目进行了应用展示。
摘要由CSDN通过智能技术生成

链接

Luogu P3567

题目描述

给一个数列,每次询问一个区间内有没有一个数出现次数超过一半

思路

其实很容易想到,这道题就是查询的时候记录一下一个数是否有超过区间长度的一半就可以了
模板修改一丢丢

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

struct trr
{
	int k, ls, rs;	
}tr[32000005];

int cnt, n, m;
int p[1000005], a[1000005], T[1000005];

void add(int &x, int last)
{
	x = ++cnt;
	tr[x] = tr[last];
}

void build(int &x, int l, int r)
{
	x = ++cnt;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(tr[x].ls, l, mid);
	build(tr[x].rs, mid + 1, r);
}

void init(int &x, int last, int l, int r, int val)
{                                             
	add(x, last);                                
	if(l == r) {                                 
		tr[x].k++;                                  
		return;                                     
	}                                            
	int mid = (l + r) >> 1;                      
	if(val <= mid) init(tr[x].ls, tr[last].ls, l, mid, val);
	if(val > mid) init(tr[x].rs, tr[last].rs, mid + 1, r, val);
	tr[x].k = tr[tr[x].ls].k + tr[tr[x].rs].k;
}

int ask(int u, int v, int l, int r, int go)
{
	int mid = (l + r) >> 1;
	int x = tr[tr[v].ls].k - tr[tr[u].ls].k;//比1大比mid小
	int xx = tr[tr[v].rs].k - tr[tr[u].rs].k;//比mid大比r小
	if(l >= r) return l;
	if(x > go) return ask(tr[u].ls, tr[v].ls, l, mid, go);
	if(xx > go) return ask(tr[u].rs, tr[v].rs, mid + 1, r, go);
	return 0;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
		scanf("%d", &a[i]), p[i] = a[i];
	sort(p + 1, p + n + 1);
	int q = unique(p + 1, p + n + 1) - p - 1;
	build(T[0], 1, q);
	for(int i = 1; i <= n; ++i) {
		int xx = lower_bound(p + 1, p + q + 1, a[i]) - p;
		init(T[i], T[i - 1], 1, q, xx);
	}
	for(int i = 1; i <= m; ++i)
	{
		int l, r;
		scanf("%d%d", &l, &r);
		printf("%d\n", p[ask(T[l - 1], T[r], 1, q, (r - l + 1) / 2)]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值