题目链接
题意
定义$f(l, r, k)$为$A[l…r]$的$k-th$大。
计算 .
思路
不妨计算每个数字的贡献,我们求出每个数字的贡献区间即可。
标程是用链表写的。我觉得其实笛卡尔树也是一个不错的选择,每次删的都是根结点,但是会T,因为如果树的形状是第一个元素次小的样子,去找那$k$个区间的时候是$O(n)$的。我还是把它贴在这里吧。
1 |
|
链表1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using namespace std;
const int N = 5e5 + 10;
typedef long long ll;
int pre[N], nxt[N], v[N], pos[N], n, k;
ll a[N], b[N];
ll solve(int x) {
int c1 = 0, c2 = 0;
for(int i = x; i && c1 <= k; i = pre[i])
a[++c1] = i - pre[i];
for(int i = x; i <= n && c2 <= k; i = nxt[i])
b[++c2] = nxt[i] - i;
ll ans = 0;
for(int i = 1; i <= c1; i++)
if(k - i + 1 <= c2 && k - i + 1 >= 1)
ans += a[i] * b[k-i+1];
return ans;
}
void del(int x) {
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
}
int main()
{
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &v[i]), pos[v[i]] = i;
for(int i = 0; i <= n + 1; i++) pre[i] = i - 1, nxt[i] = i + 1;
pre[0] = 0; nxt[n+1] = n + 1;
ll ans = 0;
for(int i = 1; i <= n; i++) {
ans += solve(pos[i]) * i;
del(pos[i]);
} printf("%lld\n", ans);
} return 0;
}