10%:
朴素做法,对于每一个
i
枚举考察前面的每一个
时间复杂度:
O(n2)
50%:
可以将绝对值符号变成分类讨论,即
显然,如果设 A1 至 Ai−1 中有 k1 个数小于 Ai ,有 k2 个数大于 Ai ,则对于 ∀Aj<Ai,Bi+=Ai−Aj ,大于 Ai 的情况同理。不难发现可以推出
因此,不妨记 s1=∑Aj ∀Aj<Ai,s2=∑Aj ∀Aj>Ai ,显然有
其中
Ai
为已知值,只需维护
k1,k2,s1
和
s2
即可算出
Bi
。问题转化为求所有满足
j<i
且
Aj<Ai
的
j
的个数
考虑到 50% 的数据
Ai≤200000
,数字较小,可以用两个 BIT,一个维护数字出现的个数以计算
k
,另一个维护具体的
时间复杂度:
100%:
做法一:离散化+50% 的做法
注意到
N≤200000
不会很大,直接根据
Ai
进行维护会导致很大的浪费,此时就可以对数列进行离散化之后,再用 50% 的做法。
时间复杂度:
O(nlog2n)
,但是常数相对做法二更大,因为预先的排序需要耗费跟求解过程一样多的时间。
做法二:Treap
利用 Treap 的 BST 性质,只需维护
cnt
、
size
(见模板题的笔记)和
sum
域,即可快速求得两个
k
和
我们用一个结点的
sum
值表示以该结点为根的子树所有结点的值之和(包含重复出现的),目标是用 Treap 求出比给定的
Ai
小的个数和它们的总和。
到达一个结点时,可以分为三种情况讨论:
- 当前结点为空,则返回 0 个数小于 Ai ,和为 0
- Ai 小于等于当前结点,则答案肯定在左子树中,递归求解左子树并返回其答案
- Ai 大于当前结点,则左子树肯定也都小于 Ai ,因此至少有 sizel+cntu 个数小于 Ai ,它们的和为 suml+cntu×valu ,再与右子树答案相加
求大于部分的过程类似,只是把判断反了过来。
注意,大于和小于应该分开求解。我一开始是大于和小于同时求,再根据需要返回,这样在搜索树中,每个结点都会扩展出两棵子树,复杂度就变成了
O(2n)
。
事实上,对于一个确定的结点,它要么小于等于
Ai
,要么大于,而可以根据情况递归求解唯一不确定的子树(一定有其中一棵子树的答案是可以确定的。想一想,为什么。)这样递归次数只与 Treap 的深度有关,增长速度比较慢。或者也可以考虑把求解小于和大于的过程分开实现,可能会更清晰一些。
时间复杂度:
O(nlog2n)
参考代码:
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
typedef pair <int, long long> pil;
const int MAXN = 2e5 + 100;
const int MOD = 1000000007LL;
/*
struct QueryResult {
int less_cnt, greater_cnt;
long long less_sum, greater_sum;
};
*/
struct Tnode {
Tnode *child[2];
long long val, sum; int fix;
int cnt, siz;
Tnode (long long v = 0LL) : val(v), sum(v), fix(rand()), cnt(1), siz(1) {
child[0] = child[1] = 0;
}
void update() {
int lsiz = (child[0] ? child[0] -> siz : 0);
int rsiz = (child[1] ? child[1] -> siz : 0);
siz = cnt + lsiz + rsiz;
int lsum = (child[0] ? child[0] -> sum : 0);
int rsum = (child[1] ? child[1] -> sum : 0);
sum = (lsum + rsum + cnt * val % MOD) % MOD;
}
};
struct Treap {
Tnode *root;
Treap () : root(0) {}
void rotate(Tnode *&cur, int dir) {
Tnode *ch = cur -> child[dir ^ 1];
cur -> child[dir ^ 1] = ch -> child[dir];
ch -> child[dir] = cur;
cur -> update();
ch -> update();
cur = ch;
}
void insert_val(Tnode *&cur, int v) {
if (!cur) cur = new Tnode(v);
else {
if (v == cur -> val) ++(cur -> cnt);
else {
int t = v > cur -> val;
insert_val(cur -> child[t], v);
if (cur -> child[t] -> fix < cur -> fix) rotate(cur, t ^ 1);
}
cur -> update();
}
}
/*
QueryResult query(Tnode *cur, int v) {
if (!cur) return (QueryResult){0, 0, 0LL, 0LL};
QueryResult lres = query(cur -> child[0], v), rres = query(cur -> child[1], v);
int lsiz = (cur -> child[0] ? cur -> child[0] -> siz : 0);
int rsiz = (cur -> child[1] ? cur -> child[1] -> siz : 0);
int lsum = (cur -> child[0] ? cur -> child[0] -> sum : 0);
int rsum = (cur -> child[1] ? cur -> child[1] -> sum : 0);
if (cur -> val == v) return (QueryResult){lres.less_cnt, rres.greater_cnt, lres.less_sum, rres.greater_sum};
else if (v < cur -> val) return (QueryResult){lres.less_cnt, lres.greater_cnt + cur -> cnt + rsiz, lres.less_sum, ((lres.greater_sum + cur -> val * cur -> cnt % MOD) % MOD + rsum) % MOD};
else return (QueryResult){lsiz + cur -> cnt + rres.less_cnt, rres.greater_cnt, ((lsum + cur -> val * cur -> cnt % MOD) % MOD + rres.less_sum) % MOD, rres.greater_sum};
}
*/
pil query_min(Tnode *cur, int v) { //在以 cur 为根的子树中找比 v 小的数,返回值的 first 为个数,second 为总和
if (!cur) return make_pair(0, 0); //空结点
if (v <= cur -> val) return query_min(cur -> child[0], v); //比当前结点值还要小,只有在左子树中才可能有比 v 小的值
else {
pil t = query_min(cur -> child[1], v); //比当前结点大,左子树和当前结点都比 v 小,且右子树中可能还存在比 v 小的值
int lsiz = (cur -> child[0] ? cur -> child[0] -> siz : 0);
long long lsum = (cur -> child[0] ? cur -> child[0] -> sum : 0LL);
return make_pair(lsiz + cur -> cnt + t.first, ((lsum + cur -> val * cur -> cnt % MOD) % MOD + t.second) % MOD); //合并答案
}
}
pil query_max(Tnode *cur, int v) { //同理,只是都反了过来
if (!cur) return make_pair(0, 0LL);
if (v >= cur -> val) return query_max(cur -> child[1], v);
else {
pil t = query_max(cur -> child[0], v);
int rsiz = (cur -> child[1] ? cur -> child[1] -> siz : 0);
long long rsum = (cur -> child[1] ? cur -> child[1] -> sum : 0LL);
return make_pair(t.first + cur -> cnt + rsiz, ((t.second + cur -> val * cur -> cnt % MOD) % MOD + rsum) % MOD);
}
}
void debug_output(Tnode *&cur) { //调试输出,中序遍历以 cur 为根的 Treap
putchar('(');
if (cur -> child[0]) debug_output(cur -> child[0]);
putchar(')');
printf("%lld ", cur -> val);
putchar('(');
if (cur -> child[1]) debug_output(cur -> child[1]);
putchar(')');
}
} lkb_treap;
int N;
long long A[MAXN];
int main(void) {
freopen("2166.in", "r", stdin);
freopen("2166.out", "w", stdout);
scanf("%d", &N);
for (int i = 0; i < N; i++) scanf("%lld", &A[i]);
long long ans = 1, sum = A[0]; lkb_treap.insert_val(lkb_treap.root, A[0]); //将第一个数作为根
for (int i = 1; i < N; i++) {
// QueryResult ret = lkb_treap.query(lkb_treap.root, A[i]);
pil mn = lkb_treap.query_min(lkb_treap.root, A[i]), mx = lkb_treap.query_max(lkb_treap.root, A[i]);
long long B = ((mn.first * A[i] % MOD - mn.second + MOD) % MOD + (mx.second - mx.first * A[i] % MOD + MOD) % MOD) % MOD;
// printf("%lld\n", B);
(ans *= B) %= MOD; //虽然本题中不会影响答案,但先计算再更新比先更新再计算更稳妥
lkb_treap.insert_val(lkb_treap.root, A[i]);
// lkb_treap.debug_output(lkb_treap.root); putchar('\n');
}
printf("%lld\n", ans);
return 0;
}