题解:CF2056D Unique Median

解析

注意到 \(a_i\) 很小,那就不妨让它更小一点,从最简单的情况出发。

\(1 \le a_i \le 1\) 时,显然所有子数组都是”好数组“。

\(1 \le a_i \le 2\) 时,如果一个子数组中 \(1\) 的个数与 \(2\) 的个数不同,那么它是“好数组”,否则它不是“好数组”。

感觉个数相同的情况会更少,并且似乎会更好求,那就求它!

问题就变成找满足区间内 \(1\)\(2\) 个数相等的区间个数。

对于区间问题,有一种常见的做法——把它转化成前缀的问题。设 \(f(x)\) 表示 \(a[1,x]\) 中,\(1\) 的出现次数比 \(2\) 多多少次,那么如果有 \(y < x\) 满足 \(f(y) = f(x)\),那么 \(a[y + 1,x]\) 就不是一个 "好数组",因为把长度为 \(y\) 的前缀去掉就相当于去掉了这一部分的贡献,剩下这一部分 \(1\) 的个数就和 \(2\) 的个数相等了。

尝试将这种做法推广到 \(1 \le a_i \le 10\) 的情况,如果子数组内 \(\le x\) 的元素个数等于 \(> x\) 的元素个数,且子数组内有元素 \(x\),则该子数组不是一个“好数组”,详见代码。

代码

/*
cnt[i][j] 表示前 i 个元素, 1 ~ j 的个数比 j + 1 ~ 10 的多多少个
不合法的个数就是满足 cnt[i][j] - cnt[k][j] = 0 (k < i)的个数
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 100000 + 5;
typedef pair<int,int> pii;
typedef long long ll;
int a[N];
int cnt[N][15],num[N],last[N];//num[i] 表示当前 1 ~ i 的元素个数,last[i] 表示元素 i 最后出现的位置
map<pii,vector<int> > m;//m[i][j] 存储的是 <= i 的元素个数比 > i 的元素个数多 j 个的前缀位置
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		m.clear();
		for(int i=1;i<=9;i++){
			m[{i,0}].push_back(0);
			num[i] = 0;
			last[i] = 0;
		}
		int n;
		cin>>n;
		ll res = 1ll * n * (n + 1) / 2;//总子数组个数
		for(int i=1;i<=n;i++){
			cin>>a[i];
			last[a[i]] = i;
			for(int j=a[i];j<=9;j++){
				num[j]++;
			}
			for(int j=1;j<=9;j++){
				cnt[i][j] = num[j] - (i - num[j]);
				if(last[j] && m.count({j,cnt[i][j]})){
					int pos = lower_bound(m[{j,cnt[i][j]}].begin(),m[{j,cnt[i][j]}].end(),last[j]) - m[{j,cnt[i][j]}].begin();
          //需要保证剔除前缀后的区间内确实有 j 这个元素,所以要求 < last[j] 的位置个数
					res -= pos;
				}
			}
			for(int j=1;j<=9;j++){
				m[{j,cnt[i][j]}].push_back(i);
			}
		}
		cout<<res<<'\n';
	}
	return 0;
}
posted @ 2025-08-20 06:35  yutar  阅读(5)  评论(0)    收藏  举报