HDU多校5
1009 Array(树状数组)
描述:
给定序列,找存在绝对众数的区间 (区间的众数出现次数大于区间长度一半) 个数。
分析:
本题与洛谷上一题除了数据范围是一模一样的。
其题解。
这题同样用线段树求理论上是可以过的,但是线段树常数可能大一下,以至于会被卡TLE 但是实验室有人线段树过了。
考虑用常数更小一点的树状数组。
要求是要维护-1,1序列的sum数组,做到区间修改,区间查询二阶前缀和。
然后在树状数组中要想做到区间修改,只能对差分数组进行单点修改,这样原序列就变成了差分数组的一阶前缀和,要实现的区间查询二阶前缀和变成了三阶前缀和。
设 a,b,c,d分别为原数组(差分数组),一阶前缀和数组,二阶前缀和数组,三阶前缀和数组,那么有:
d [ x ] = ∑ i = 1 x ∑ j = 1 i ∑ k = 1 j a [ k ] d[x]=\sum_{i=1}^x\sum_{j=1}^i\sum_{k=1}^ja[k] d[x]=∑i=1x∑j=1i∑k=1ja[k]
然后分析一下这个式子:
∑ j = 1 i ∑ k = 1 j a [ k ] \sum_{j=1}^i\sum_{k=1}^ja[k] ∑j=1i∑k=1ja[k]
=
a
1
+
=a_1 +
=a1+
a
1
+
a
2
+
\ \ \ \ a_1+a_2+
a1+a2+
a
1
+
a
2
+
a
3
+
\ \ \ \ a_1+a_2+a_3+
a1+a2+a3+
.
.
.
+
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ...\ \ \ \ \ \ +
... +
a
1
+
a
2
+
a
3
+
.
.
.
+
a
i
\ \ \ \ a_1+a_2+a_3+...+a_i
a1+a2+a3+...+ai
= i a 1 + ( i − 1 ) a 2 + . . . + a i =ia_1+(i-1)a_2+...+a_i =ia1+(i−1)a2+...+ai
所以就有
d [ x ] = d[x]= d[x]=
a
1
+
a_1+
a1+
2
a
1
+
a
2
+
2a_1+a_2+
2a1+a2+
.
.
.
+
\ \ \ \ \ \ \ \ \ \ \ ...\ \ \ \ \ \ \ \ \ +
... +
i
a
1
+
(
i
−
1
)
a
2
+
.
.
.
+
a
i
ia_1+(i-1)a_2+...+a_i
ia1+(i−1)a2+...+ai
.
.
.
+
\ \ \ \ \ \ \ \ \ \ \ ...\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +
... +
x
a
1
+
(
x
−
1
)
a
2
+
.
.
.
+
2
a
x
−
1
+
a
x
xa_1+(x-1)a_2+...+2a_{x-1}+a_x
xa1+(x−1)a2+...+2ax−1+ax
可以发现最终 a 1 , a 2 , . . . , a x a_1,a_2,...,a_x a1,a2,...,ax的系数式等差数列的前缀和,运用等差数列求和公式,有:
d [ x ] = ∑ i = 1 x ( x − i + 2 ) ( x − i + 1 ) 2 a i d[x]=\sum_{i=1}^x\frac{(x-i+2)(x-i+1)}{2}a_i d[x]=∑i=1x2(x−i+2)(x−i+1)ai
= ( x + 1 ) ( x + 2 ) 2 ∑ i = 1 x a i − 2 x + 3 2 ∑ i = 1 x i a i + 1 2 ∑ i = 1 x i 2 a i \ \ \ \ \ \ \ \ =\frac{(x+1)(x+2)}{2}\sum_{i=1}^{x}a_i-\frac{2x+3}{2}\sum_{i=1}^{x}ia_i+\frac{1}{2}\sum_{i=1}^{x}i^2a_i =2(x+1)(x+2)∑i=1xai−22x+3∑i=1xiai+21∑i=1xi2ai
最后像线段树做法一样加上一个偏移量,做区间修改区间查询就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pli pair<ll,int>
#define Min(a,b,c) min(a,min(b,c))
#define Max(a,b,c) max(a,max(b,c))
typedef long long ll;
typedef unsigned long long ull;
const double pi = 3.141592653589793;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int N = 1000010;
int n, del, a[N];
vector<int> vec[N];
ll c1[N << 1], c2[N << 1], c3[N << 1];
ll ask(int x)
{
ll ans = 0;
for (int i = x; i; i -= i & -i)
ans += (ll)(x + 1) * (x + 2) * c1[i] - (ll)(2 * x + 3) * c2[i] + c3[i];
return ans / 2;
}
void change(int x, int d)
{
for (int i = x; i <= n + del; i += i & -i)
{
c1[i] += d;
c2[i] += (ll)x * d;
c3[i] += (ll)x * x * d;
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
int mx = 0, mn = INF;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
mx = max(mx, a[i]);
mn = min(mn, a[i]);
vec[a[i]].push_back(i);
}
del = n + 2;
ll ans = 0;
for (int i = mn; i <= mx; i++)
{
if (vec[i].empty()) continue;
vec[i].push_back(n + 1);
int pre = 0, sum = 0;
for (int j = 0; j < vec[i].size(); j++)
{
int now = vec[i][j] - 1;
int low = sum - (now - pre), up = sum;
ans += ask(up - 1 + del) - ask(low - 2 + del);
change(low + del, 1);
change(up + 1 + del, -1);
pre = now + 1;
sum = low + 1;
}
pre = 0, sum = 0;
for (int j = 0; j < vec[i].size(); j++)
{
int now = vec[i][j] - 1;
int low = sum - (now - pre), up = sum;
change(low + del, -1);
change(up + 1 + del, 1);
pre = now + 1;
sum = low + 1;
}
vec[i].clear();
}
printf("%lld\n", ans);
}
return 0;
}