P8313 [COCI 2021/2022 #4] Izbori 题解

题意,即求有多少区间存在绝对众数。考虑分治,每次求经过 \(mid\) 的好区间的个数。

注意到若 \([l,r]\) 存在绝对众数,则 \([l,mid]\)\([mid+1,r]\) 必有一个存在绝对众数。固定一个端点,绝对众数的个数是 \(\mathcal O(\log n)\) 的。

于是可以处理出所有可能的绝对众数,令这个数为 \(+1\),其它数为 \(-1\),从 \(mid\) 往两边求和,对于某一边统计一个前缀和,另一边直接 \(\mathcal O(1)\) 求个数。

时间复杂度 \(\mathcal O(n\log^2 n)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 200003
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,mx,a[mxn],b[mxn],c[mxn];
ll ans;
set<int>s;
void solve(int l,int r){
	if(l==r){
		ans++;
		return;
	}
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	s.clear();
	drep(i,mid,l)c[a[i]]=0;
	c[mx=0]=0;
	drep(i,mid,l){
		if(++c[a[i]]>c[mx])mx=a[i];
		if(c[mx]>(mid-i+1)>>1)s.insert(mx);
	}
	c[mx=0]=0;
	rep(i,mid+1,r)c[a[i]]=0;
	rep(i,mid+1,r){
		if(++c[a[i]]>c[mx])mx=a[i];
		if(c[mx]>(i-mid)>>1)s.insert(mx);
	}
	for(int cl:s){
		int ct=0;
		rep(i,0,((r-mid)<<1)+1)c[i]=0;
		rep(i,mid+1,r){
			ct+=a[i]==cl?1:-1;
			c[ct+r-mid]++;
		}
		drep(i,(r-mid)<<1,0)c[i]+=c[i+1];
		ct=0;
		drep(i,mid,l){
			ct+=a[i]==cl?1:-1;
			if(1-ct+r-mid<=(r-mid)<<1)ans+=c[max(1-ct+r-mid,0)];
		}
	}
}
signed main(){
	scanf("%d",&n);
	rep(i,1,n)scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	rep(i,1,n)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
	solve(1,n);
	cout<<ans;
	return 0;
}
posted @ 2025-07-05 17:08  zifanwang  阅读(9)  评论(0)    收藏  举报