摩尔投票的扩展

摩尔投票的扩展

以下做法只能保证合法的数字一定会出现在答案中,不保证答案中的数字都是合法的。

Problem A. 找到集合中出现次数 \(>\frac{n}{3}\) 的数字

注意到序列中合法的数字个数一定不会大于两个。

我们把所有相同的数字分到一个集合中,此时会生成 \(\leq n\) 个集合。

我们每次选取三个不同的集合,并从这些集合中分别删除一个元素,若集合删空则删除这个集合。重复执行以上操作,直到只剩下 \(\leq 2\) 个集合。

上面做法的正确性在于,对于一个大小 \(>\frac{n}{3}\) 的集合,需要进行 \(>\frac{n}{3}\) 次操作才能将其删空,但显然我们完不成这么多操作。那么合法的集合一定会在最后剩下来。

类似于普通版本的摩尔投票,那么我们维护两个数字 \(v1,v2\) 以及它们的出现次数 \(c_1,c_2\)

考虑如何加入一个新数字 \(x\)。分以下几种情况讨论:

  1. \(c_1=0\),令 \(v_1\leftarrow x,c_1 \leftarrow 1\)
  2. \(x=v_1\),令 \(c_1\leftarrow c_1+1\)
  3. \(c_2=0\),令 \(v_2\leftarrow x,c_2 \leftarrow 1\)
  4. \(x=v_2\),令 \(c_2\leftarrow c_2+1\)
  5. otherwise,令 \(c_1\leftarrow c_1-1,c_2\leftarrow c_2-1\)

Problem B. 找到集合中出现次数 \(\geq p \%\) 的数字

\(k=\lfloor\frac{100}{p} \rfloor\),序列中合法数字个数不超过 \(k\) 个。

那么我们每次选择 \(k+1\) 个集合进行操作,重复执行直到剩下 \(\leq k\) 个集合。

对于一个大小 \(\geq p\%\) 的数字,至少需要操作 \(n\times p\%\) 次。由于 \(n \times p\% \times (\lfloor \frac{100}{p}\rfloor+1)>n\),所以合法的数字一定会剩下来。

我们此时需要维护一个二元组集合 \(S\),其中每个元素 \((v,c)\) 记录了这个数字的值以及出现次数。

考虑加入 \(x\),分类讨论:

  1. \(S\) 中存在 \(v=x\),令 \(c\leftarrow c+1\)
  2. \(|S| <k\),向 \(S\) 中加入 \((x,1)\)
  3. \(S\) 中所有 \(c \leftarrow c-1\),若 \(c=0\) 则删除该元素。

Problem C. 区间赋值,区间查询出现次数 \(\geq p\%\) 的数字。

可以发现,无论以什么方式对这些集合进行操作,合法的数字一定会剩下来。那么摩尔投票就有了结合律。

合并两个区间是简单的,类似于上面的做法,我们把 \(Sr\) 中的二元组依次加入 \(Sl\) 中,分情况讨论即可。

Node Pushup(Node ls,Node rs){
	for(int i=0;i<B;i++){
		if(!rs.c[i]) continue;
		bool ok=0;
		for(int j=0;j<B;j++) if(ls.v[j]==rs.v[i]){ls.c[j]+=rs.c[i],ok=1;break;}
		if(ok) continue; ok=0; int mn=rs.c[i];
		for(int j=0;j<B;j++){Ckmin(mn,ls.c[j]);if(!ls.c[j]){ls.v[j]=rs.v[i],ls.c[j]=rs.c[i],ok=1;break;}}
		if(ok) continue;
		for(int j=0;j<B;j++) ls.c[j]-=mn; 
		rs.c[i]-=mn; if(!rs.c[i]) continue;
		for(int j=0;j<B;j++) if(!ls.c[j]){ls.v[j]=rs.v[i],ls.c[j]=rs.c[i];break;}
	}
	return ls;
}

posted @ 2025-02-20 15:07  XP3301_Pipi  阅读(72)  评论(3)    收藏  举报
Title