题目大意
给定长为 \(n\) 的序列,求包含绝对众数的区间个数。
题解
我们可以分别考虑每个数字为绝对众数时产生的贡献。考虑一个数字 \(x\),根据摩尔投票法的思想,我们可以将序列中所有 \(a_i=x\) 的位置赋值为 \(1\),其余位置赋值为 \(-1\),问题转化为求区间和大于 \(0\) 的区间数量。
因为是区间和,所以考虑前缀和转化,即求在前缀和数组上的顺序对 \((s_i,s_j)\)(\(i<j,s_i<s_j\))的数量。如果不考虑时间复杂度,可以使用权值树状数组,通过单修区查实现。
但是因为我们枚举了众数的值,所以对于数字 \(x\) 只能枚举该 \(x\) 所在的那些位置(即不枚举其他位置),才能保证均摊时间复杂度的正确性。
容易发现,\(x\) 出现的一个位置到下一次出现之前,中间的一段的前缀和 \(s\) 数组,是一段公差为 \(-1\) 的等差数列。等差数列的修改对应在值域上是一段区间修改,其查询可以表示为 \(\sum_{i=l}^r{sum(i-1)}\),这是一个二阶前缀和。我们可以通过爆改树状数组,实现区间加,区间二阶前缀和查询。推一下公式即可。
AC 代码
| #include<iostream>
#include<vector>
#define int long long
using namespace std;
const int N = 5E5 + 10;
vector<int> pos[N];
int n, tp;
int a[N];
namespace BIT {
int sum1[2 * N], sum2[2 * N], sum3[2 * N];
inline int lowbit(int x) { return x & -x; }
inline void modify(int p, int v) {
for(int i = p + N; i <= 2 * N - 1; i += lowbit(i)) {
sum1[i] += v;
sum2[i] += p * v;
sum3[i] += p * p * v;
}
}
inline void modify(int l, int r, int v) {
modify(l, v);
modify(r + 1, -v);
}
inline int query(int p) {
int res = 0;
for(int i = p + N; i > 0; i -= lowbit(i)) {
res += (p * p + 3 * p + 2) * sum1[i];
res -= (2 * p + 3) * sum2[i];
res += sum3[i];
}
res /= 2;
return res;
}
inline int query(int l, int r) {
return query(r) - query(l - 1);
}
}
signed main() {
cin >> n >> tp;
for(int i = 0; i <= n - 1; i++) pos[i].push_back(0);
for(int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]].push_back(i);
}
for(int i = 0; i <= n - 1; i++) pos[i].push_back(n + 1);
int ans = 0;
for(int i = 0; i <= n - 1; i++) {
int cur = -1;
for(int j = 0; j < (int)pos[i].size() - 1; j++) {
cur++;
int l = cur - (pos[i][j + 1] - pos[i][j]) + 1, r = cur;
ans += BIT::query(l - 1, r - 1);
BIT::modify(l, r, 1);
cur = l;
}
cur = -1;
for(int j = 0; j < (int)pos[i].size() - 1; j++) {
cur++;
int l = cur - (pos[i][j + 1] - pos[i][j]) + 1, r = cur;
BIT::modify(l, r, -1);
cur = l;
}
}
cout << ans << endl;
return 0;
}
|