未看懂请放心评论,将在几个小时内进行回复
题目
题意
给定一个序列,大小为,求它的满足的子段个数(即上取整中位数和下取整中位数相等的子段个数)
解法
- 奇数长度的子段一定满足要求
- 我们可以将答案转化为:所有区间的个数-不满足要求的区间个数。
- 不满足要求的区间为【。。。,。。。】型的区间。分别为下取整中位数和上取整中位数
- 这些不满足要求的区间可以被划分为下取整中位数为1,2,3,4,5...10的共计10种类型。
- 所以问题转化为我们怎么求出中位数为的不满足要求的区间个数了。
- 转化:中位数为k时,意味着小于等于k的数字数量刚好等于大于k的数字数量,所以如果我们记作+1,我们记作-1,当一个区间内和为0的时候就意味着仅有下取整中位数为。
- 并且当一个区间仅有下取整中位数为的时候,也意味着,长度一定为偶数,因为长度为奇数的时候,一定不会出现区间和为0。
- 但是要注意,这个区间是需要满足两个条件的 ①区间和为0 ②区间内包含这个数字
- 条件①我们可以使用前缀和记录的方式较为轻松的得到,而条件②我们可以将一个区间看成这种样子,其中表示第几个,我们在和中间只能使用之前求出的前缀值,因为要保证的数量>1。所以我们可以每到一个新的就更新一下前缀和记录数组。
代码
#include <bits/stdc++.h>
using namespace std;
#define inf64 INT64_MAX/2
#define inf32 INT32_MAX/2
#define ll long long
#define int ll
#define pii pair<int,int>
#define endl '\n'
#define vv vector
#define cy cout<<"Yes"<<endl
#define cn cout<<"No"<<endl
#define its(a) a.begin(),a.end()
#define minV *min_element
#define maxV *max_element
int _;
void solve() {
int n; cin >> n;
vv<int>a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
auto get=[&](int k) -> int {
map<int, int>cnt_last; //合法的前缀和数量记录
vv<int>Do; //在这个新增区间内的操作
int now = 0; //中位数为k的坏区间的个数
int s = 0; //当前的和值
Do.push_back(s); //新增的操作
for (int i = 1; i <= n; i++) {
if (a[i] == k) { //将新增的操作加入前缀和记录中
for (auto a : Do)
cnt_last[a]++;
Do.clear();
}
if (a[i] <= k)
s++;
else
s--;
now += cnt_last[s];
//记录操作
Do.push_back(s);
}
return now;
};
int ans = 0;
for (int i = 1; i <= 10; i++) {
ans += get(i);
}
cout << (n + 1) * n / 2 - ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
_ = 1;
cin >> _;
while (_--) {
solve();
}
return 0;
}