题解 P3567 [POI2014]KUR-Couriers

题意翻译

给一个长度为 \(n\) 的正整数序列 \(a\)。共有 \(m\) 组询问,每次询问一个区间 \([l,r]\) ,是否存在一个数在$ [l,r]$ 中出现的次数严格大于一半。如果存在,输出这个数,否则输出 \(0\)

\(1 \leq n,m \leq 5 \times 10^5,0 \leq a_i \leq n.\)

分析

是主席树的板子题,不过用莫队也可以做。

这里讲一下主席树的做法。(其实也没有什么做法)。。

利用前缀和的性质,我们可以得到 \([L,R]\) 中的元素组成的权值线段树。

从根节点开始,

我们假设存在 该严格众数。

那么

  • 该数的出现次数 \(>(r-l+1)/2\) ----------> 该数所在区间的 \(sum > (r-l+1)/2\)
  • 又某节点的左右子树中至多只有一个满足该性质,(总共才 \((r-l+1)\) 个元素)。
  • 所以,如果有子树满足该性质,那么往这个子树走,
  • 如果一个都没有,那么不存在严格众数,返回0.

Code:

#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define rint register int
using namespace std;
namespace FastIO
{
char c;
bool sign;
template<class T>
inline void read(T &x)
{
	x=0;
	sign=false;
	for(c=getchar();c<'0'||c>'9';c=getchar()) 
		if(c=='-') 
			sign=true;
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c&15);
	if(sign) x=~x+1;
 	return;
}
template<class T>
void print(T x)
{
	if(x<0) putchar('-'),x=~x+1;
	if(x>9) print(x/10);
	putchar(x%10+'0');
	return;
}
}
using FastIO::read;
using FastIO::print;
//================================================
const int N=5e5+5;
struct Node
{
	int lc,rc;
	int sum;
}hjt[32*N];
int cnt=0;
int root[N];
void insert(int &now,const int &pre,const int &key,const int &l,const int &r)
{
	hjt[now=++cnt]=hjt[pre];
	hjt[now].sum++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(key<=mid) insert(hjt[now].lc,hjt[pre].lc,key,l,mid);
	else insert(hjt[now].rc,hjt[pre].rc,key,mid+1,r);
	return;
}
int query(const int &L,const int &R,const int &l,const int &r,const int &lit) //(L,R]
{
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(hjt[hjt[R].lc].sum-hjt[hjt[L].lc].sum>lit) 
		return query(hjt[L].lc,hjt[R].lc,l,mid,lit);
	else if(hjt[hjt[R].rc].sum-hjt[hjt[L].rc].sum>lit)
		return query(hjt[L].rc,hjt[R].rc,mid+1,r,lit);
	else return 0;
}
int a[N];
int n,m;
vector<int> v;
int main()
{
//	freopen("1.in","r",stdin);
	rint i;
	int x,y,z;
	read(n); read(m);
	for(i=1;i<=n;i++) {
		read(a[i]);
		v.push_back(a[i]);
	}
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	for(i=1;i<=n;i++) 
		a[i]=(int)(lower_bound(v.begin(),v.end(),a[i])-v.begin())+1;
	for(i=1;i<=n;i++) 
		insert(root[i],root[i-1],a[i],1,v.size());
	while(m--) {
		read(x); read(y);
		z=query(root[x-1],root[y],1,v.size(),(y-x+1)>>1);
		if(z==0) puts("0");
		else print(v[z-1]),putchar('\n');
	}
	return 0;
}

虽然题目的数据范围不需要离散化,但我还是离散化了一下,(但愿会快一点)。

另外第一次用 \(scanf\)\(printf\) 交上去T了,

\(5 \times 10^{5}\) 的数据范围下,用快读快输大概会比 \(scanf \space printf\) 稳定的快上 \(100ms - 150ms\)


P.S:还有两种很巧妙的做法。

posted @ 2020-06-21 13:29  cjlworld  阅读(173)  评论(0编辑  收藏  举报