题解:洛谷 P3567([POI 2014] KUR-Couriers)

Link

1. Description

定义一个长度为 \(len\) 的序列的绝对众数为:出现次数大于 \(\frac {len}2\) 的数值,现在给定一个长度为 \(n\) 的序列 \(a\),并给定 \(m\) 个询问,每一次询问 \(a\) 的子序列 \([l,r]\) 的绝对众数,如果绝对众数不存在,那么输出 \(0\)

2. Solution

这里给出三种做法,其中一种是离线做法,另外两种是在线做法。

1.分治的离线做法

看到绝对众数,我们可以想到另外一道题,因此想到分治做法。

先将所有询问离线,假设现在我们分治递归到了 \([l,r]\) 这个区间,处理左端在 \([l,mid]\),右端在 \([mid+1,r]\) 的询问,那么可能在 \([l,r]\) 这个区间中的任意一个子区间内成为绝对众数的数其实十分少,只有 \(\log n\) 个,我们可以直接记录下来所有可能成为绝对众数的数,然后对于所有询问,我们检查每一个数 \(x\) 是否是询问区间的绝对众数即可,具体的检查方法为,将 \(x\) 视为 \(1\),将其他数视为 \(-1\),然后看看区间和是不是大于零即可,可以使用前缀和 \(O(len)\) 预处理,然后 \(O(1)\) 查询。

时间复杂度不太确定,但是跑的还是挺快的。

2.线段树的在线做法

求绝对众数,我们可以使用摩尔投票法(不知道摩尔投票法可以百度一下),那么我们使用线段树维护每一个区间的摩尔投票的结果,不过需要注意的是,当绝对众数不存在的时候,摩尔投票法得到的结果是不正确的,所以得到结果之后一定要检查一下是否合法,具体的,使用 vector 记录每一个数值出现的位置,然后二分求出区间中这个数值的出现次数,时间复杂度为 \(O((m+n)\log n)\)

3.主席树的在线做法

首先我们对序列按照权值建出主席树,然后考虑对于一个区间 \([l,r]\) 如何求解,分类讨论。

  1. 如果现在已经到了叶子节点,那么直接返回。
  2. 如果左儿子出现的数的个数大于 \(\frac{r-l+1}{2}\),那么向左儿子递归。
  3. 如果右儿子出现的数的个数大于 \(\frac{r-l+1}{2}\),那么向右儿子递归。
  4. 否则,返回 \(0\)

3. Code

1.分治的离线做法

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=5e5+5;
int n,m;
int a[N],ans[N],cnt[N],pre[N];
bool vis[N];
vector<int>val;
struct Query{
	int l,r,id;
}b[N],c[N];
vector<Query>now;
void count(int x,int l,int r){
	pre[l-1]=0;
	for(int i=l;i<=r;i++)pre[i]=pre[i-1]+(a[i]==x?1:-1);
	for(Query tmp:now)
		if(pre[tmp.r]-pre[tmp.l-1]>0)
			ans[tmp.id]=x;
}
void solve(int l,int r,int ql,int qr){
	if(ql>qr)return ;
	if(l==r)return ;
	int mid=l+r>>1;
	now.clear();
	val.clear();
	int tmpql=ql-1,tmpqr=qr+1;
	for(int i=ql;i<=qr;i++){
		if(b[i].r<=mid)c[++tmpql]=b[i];
		else if(b[i].l>mid)c[--tmpqr]=b[i];
		else now.push_back(b[i]);
	}
	for(int i=mid;i>=l;i--){
		cnt[a[i]]++;
		if(cnt[a[i]]*2>(mid-i+1)&&!vis[a[i]]){
			vis[a[i]]=1;
			val.push_back(a[i]);
		}
	}
	for(int i=mid;i>=l;i--)cnt[a[i]]--;
	for(int i=mid+1;i<=r;i++){
		cnt[a[i]]++;
		if(cnt[a[i]]*2>(i-mid)&&!vis[a[i]]){
			vis[a[i]]=1;
			val.push_back(a[i]);
		}
	}
	for(int i=mid+1;i<=r;i++)cnt[a[i]]--;
	for(int x:val){
		count(x,l,r);
		vis[x]=0;
	}
	for(int i=ql;i<=tmpql;i++)b[i]=c[i];
	for(int i=qr;i>=tmpqr;i--)b[i]=c[i];
	solve(l,mid,ql,tmpql),solve(mid+1,r,tmpqr,qr);
}
signed main(){
	read(n),read(m);
	for(int i=1;i<=n;i++)read(a[i]);
	int tmp=0;
	for(int i=1,l,r;i<=m;i++){
		read(l),read(r);
		if(l==r){
			ans[i]=a[l];
			continue;
		}
		b[++tmp]={l,r,i};
	}
	solve(1,n,1,tmp);
	for(int i=1;i<=m;i++)write(ans[i]),Nxt;
}

2.线段树的在线做法

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=5e5+5;
int n,m;
int a[N];
vector<int>place[N];
struct Node{
	int x,cnt;
	Node friend operator +(Node a,Node b){
		if(a.x==0||b.x==0)return {a.x+b.x,a.cnt+b.cnt};
		if(a.x==b.x)return {a.x,a.cnt+b.cnt};
		if(a.cnt>b.cnt)return {a.x,a.cnt-b.cnt};
		if(a.cnt<b.cnt)return {b.x,b.cnt-a.cnt};
		return {0,0};
	}
};
struct Segment_tree{
	Node c[N<<2];
	#define ls p<<1
	#define rs p<<1|1
	#define mid (l+r>>1)
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void build(int p,int l,int r){
		if(l==r){
			c[p]={a[l],1};
			return ;
		}
		build(ls,l,mid),build(rs,mid+1,r);
		pushup(p);
	}
	Node query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return c[p];
		if(mid>=L&&mid<R)return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
		if(mid>=L)return query(ls,l,mid,L,R);
		return query(rs,mid+1,r,L,R);
	}
}Set;
int count(int l,int r,int x){
	auto R=upper_bound(place[x].begin(),place[x].end(),r)-1;
	auto L=lower_bound(place[x].begin(),place[x].end(),l);
	return R-L+1;
}
signed main(){
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(a[i]);
		place[a[i]].push_back(i);
	}
	Set.build(1,1,n);
	for(int i=1,l,r,ans;i<=m;i++){
		read(l),read(r);
		Node tmp=Set.query(1,1,n,l,r);
		if(tmp.x==0||count(l,r,tmp.x)*2<=r-l+1)puts("0");
		else write(tmp.x),Nxt;
	}
}

3.主席树的在线做法

const int N=5e5+5;
int n,m;
int rt[N];
struct Segment_tree{
	int num;
	int ls[N<<5],rs[N<<5],cnt[N<<5];
	#define mid (l+r>>1)
	int New(){
		num++;
		ls[num]=rs[num]=cnt[num]=0;
		return num;
	}
	int copy(int p){
		int q=New();
		ls[q]=ls[p],rs[q]=rs[p],cnt[q]=cnt[p];
		return q;
	}
	void pushup(int p){
		cnt[p]=cnt[ls[p]]+cnt[rs[p]];
	}
	int build(int l,int r){
		int p=New();
		if(l==r)return p;
		ls[p]=build(l,mid),rs[p]=build(mid+1,r);
		return p;
	}
	int change(int p,int l,int r,int x,int v){
		int q=copy(p);
		if(l==r){
			cnt[q]+=v;
			return q;
		}
		if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
		else rs[q]=change(rs[p],mid+1,r,x,v);
		pushup(q);
		return q;
	}
	int query(int p,int q,int l,int r,int len){
		if(l==r)return l;
		if(2*(cnt[ls[q]]-cnt[ls[p]])>len)return query(ls[p],ls[q],l,mid,len);
		if(2*(cnt[rs[q]]-cnt[rs[p]])>len)return query(rs[p],rs[q],mid+1,r,len);
		return 0;
	}
	void print(int p,int l,int r){
		if(l==r)return ;
		print(ls[p],l,mid);
		print(rs[p],mid+1,r);
	}
}Set;
signed main(){
	read(n),read(m);
	rt[0]=Set.build(1,n);
	for(int i=1,x;i<=n;i++){
		read(x);
		rt[i]=Set.change(rt[i-1],1,n,x,1);
	}
	Set.print(rt[n],1,n);
	for(int i=1,l,r;i<=m;i++){
		read(l),read(r);
		write(Set.query(rt[l-1],rt[r],1,n,r-l+1)),Nxt;
	}
}
posted @ 2025-04-10 21:51  陈牧九  阅读(31)  评论(0)    收藏  举报