P3567 [POI2014]KUR-Couriers
LuoguP3567 [POI2014]KUR-Couriers
菜死了,竟然没看出来这是一道主席树的模板题.还自己瞎YY了写奇技淫巧。
首先\([l,r]\)这个区间的主席树我们是很好求的。
如果一个数出现的次数严格大于\((r - l + 1) / 2\)
那么,第\((r - l + 1) /2\)大的数肯定是他,
但是,怎么判断第\((r - l + 1)/2\)大的数到底出现没出现更多次,好像没有思路。
但是,在递归查询第\(k\)大的时候,查询的排名不随区间改变而改变就好了
煮个栗子
普通的主席树如果左儿子(默认使用当前区间的主席树)有\(k - 1\)个数
我们将问题转化为查询右儿子第一大
但是这道题去查询右儿子第\(k\)大,如果右儿子也不足\(k\),说明没有数出现了\((r - l + 1) /2\)次
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cctype>
using namespace std;
const int N = 5e5 + 3;
struct node{
int sum;
int lc,rc;
}a[N * 30];
int v[N],b[N],rt[N];
int n,m,t;
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void ins(int &u,int l,int r,int x){
a[++t] = a[u];
u = t;
if(l == r){
a[u].sum ++;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid) ins(a[u].lc,l,mid,x);
else ins(a[u].rc,mid + 1,r,x);
a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;
}
inline int query(int u1,int u2,int l,int r,int x){
if(l == r) return l;
int s1 = a[a[u2].lc].sum - a[a[u1].lc].sum,s2 = a[a[u2].rc].sum - a[a[u1].rc].sum;
// printf("%d %d\n",s1,s2);
int mid = (l + r) >> 1;
if(s1 > x) return query(a[u1].lc,a[u2].lc,l,mid,x);
else if(s2 > x) return query(a[u1].rc,a[u2].rc,mid + 1,r,x);
else return 0;
}
int main(){
n = read(),m = read();
for(int i = 1;i <= n;++i) v[i] = b[i] = read();
sort(b + 1,b + n + 1);
b[0] = unique(b + 1,b + n + 1) - b - 1;
for(int i = 1;i <= n;++i){
rt[i] = rt[i - 1];
v[i] = lower_bound(b + 1,b + b[0] + 1,v[i]) - b;
ins(rt[i],1,b[0],v[i]);
}
while(m--){
int l = read(),r = read();
int k = (r - l + 1) / 2;
int ans = query(rt[l - 1],rt[r],1,b[0],k);
printf("%d\n",ans == 0 ? ans : b[ans]);
}
return 0;
}