Zechariah
2021-08-03 18:56:10
打完杭电多校看有人说有原题,就跑过来看看。
据说这个题有
由于值域比较小,考虑对每个不同的值统计合法的区间个数;又因为是统计区间个数,最直接的想法就是枚举端点,但是这样复杂度就炸掉了。
考虑对每个值 x 求众数为 x 时的区间个数,设
将式子稍作变换可得
发现左右两边形式相同,设
这个东西也可以写成二维偏序,就是
那么有一个显然的想法是枚举右端点,用树状数组求二维偏序。
接下来就要解决枚举复杂度过高的问题。
注意到所有值的总数量是
考虑一下
这样的以每个值为
单独考虑一段,在枚举右端点的过程中,
于是考虑在值域上建树状数组,显然值域是[-n, n],由于有负数,我们将其+n+1变成[1, 2n + 1]的值域。
然后就是要用树状数组维护求区间三次前缀和、区间加的操作,区间加很简单,直接维护差分数组
这里就顺便写一下树状数组维护高次前缀和的通法,我们将这个式子进行一些运算
实际上这一步只是将后面两个
那么对于
所以分开维护就好了。
那么写到这里解法就很清楚了,我们直接枚举值为
由于枚举的位置总数是
ll c0[N], c1[N], c2[N];
int a[N], top;
pair<int, int>st[N];
vector<int>loc[N >> 1];
void update(ll x, int n, ll data) {
ll i = x * data, ii = x * x * data;
while (x <= n) {
c0[x] += data;
c1[x] += i;
c2[x] += ii;
x += lowbit(x);
}
}
ll query(ll x) {
ll ans = 0, k1 = x * x + 3 * x + 2, k2 = 2 * x + 3;
while (x) {
ans += k1 * c0[x] - k2 * c1[x] + c2[x];
x -= lowbit(x);
}
return ans >> 1;
}
int main() {
int n = fast_IO::read(), m = 0; fast_IO::read();
for (int i = 1; i <= n; ++i)loc[a[i] = fast_IO::read()].push_back(i), m = max(m, a[i]);
ll ans = 0;
for (int i = 0; i <= m; ++i) {
if (loc[i].empty())continue;
int now = n + 1, pre = 0;
loc[i].push_back(n + 1);
for (auto j : loc[i]) {
ll del = query(now - 1) - query(now - (j - pre) - 1);
ans += del;
update(now - (j - pre) + 1, (n << 1) | 1, 1);
update(now + 1, (n << 1) | 1, -1);
st[++top] = make_pair(now - (j - pre) + 1, -1);
st[++top] = make_pair(now + 1, 1);
now = now - (j - pre) + 2;
pre = j;
}
//清空一下树状数组
while (top) {
update(st[top].first, (n << 1) | 1, st[top].second);
--top;
}
}
printf("%lld\n", ans);
return 0;
}