[LnOI2019]来者不拒,去者不追
Description
给定 n 个数和 m 次询问,每次询问给定 l , r ,要求区间内从小到大排序后对 排名 * 数值 求和。
避免歧义,举个例子:数列 \(1\ 2\ 2\ 3\) 的答案为 \(1 \cdot 1 + 2 \cdot 2 + 2 \cdot 2 + 4 \cdot 3 = 21\) (前者为排名,后者为数值)
\(1 \leq n,\ m \leq 5 \cdot 10 ^ 5\ \ \ 1 \leq a_i \leq 10 ^ 5\)
Analysis
因为添加一个数或删去一个数影响贡献的是一整个后缀,所以 \(\log\) 的算法显得不太现实,考虑莫队。
从普通莫队开始思考,由于每次更改一个数影响的式后缀,所以树状数组是比较好的解决方式。
因为相同的数排名是一样的,所以直接维护两个东西,一个是后缀和,因为比它大的数排名全部加一了,所以新贡献就是后缀和;另一个是排名,用来找当前数的位置,也就是这个数的贡献了。整体做下来就可以完成此题了。
时间复杂度 \(O(n\cdot\sqrt{m}\cdot\log a_{max})\)
Solution
很显然上述方式不能通过本题(听说原来可以但是现在加强数据了)
树状数组复杂度已经到头了,所以要从莫队方式上进行变更,考虑二次离线:
写法参照的是 Yuno loves sqrt technology II ,维护的东西和本题树状数组的是一致的。
然后还是要预处理 \([1, l]\) 的答案和 \([r, n]\) 的答案前缀和,就可以先把更改的区间 \([r1, r2]\) 的内部的答案先算好。
但是同样要注意的是即使我们把所有操作保存下来,也还是不能直接做,我们需要进行值域分块,然后前后各扫一遍就行了。
时间复杂度 \(O(n\log n + n\sqrt{n})\)
另一个想法
其实 Solution 里面提到的那道二次离线求逆序对,每次更改一个数是对区间内所有数有影响,还不能一个一个去比较,因为它同时和位置和大小同时相关。
但是这道题不太一样的就是每个数对区间内数的贡献仅限于大小的影响,所以条件本质上是变宽松了。
甚至可以考虑只用普通莫队做这题,但是我不太会,有会的大佬求教教 qwq 。
Code
/*
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll lim, a[N], par1[N], poi1[N], par2[N], poi2[N];
int n, m, id[N], boc, blo[N], lt[N], rt[N];
int lr[N], rl[N], bel[N], lth;
ll arr1[N], arr2[N], pre[N], suf[N], ans[N];
struct mdzz {
int op, l, r, id;
};
vector<mdzz> lp[N], rs[N];
inline int read() {
char ch = getchar();
int s = 0, w = 1;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
return s * w;
}
inline bool cmp(int x, int y) {
return blo[x] ^ blo[y] ? lt[x] < lt[y] : rt[x] < rt[y];
}
inline int lowbit(int x) {return x & (-x);}
inline void add1(int x, int k) {
for (; x <= lim; x += lowbit(x)) arr1[x] += k;
}
inline ll query1(int x) {
ll tmp = 0;
for (; x >= 1; x -= lowbit(x)) tmp += arr1[x];
return tmp;
}
inline void add2(int x, int k) {
for (; x <= lim; x += lowbit(x)) arr2[x] += k;
}
inline ll query2(int x) {
ll tmp = 0;
for (; x >= 1; x -= lowbit(x)) tmp += arr2[x];
return tmp;
}
inline void pushup(int x) {
if (par1[bel[x]]) {
for (int i = lr[bel[x]]; i <= rl[bel[x]]; ++i) poi1[i] += par1[bel[x]];
par1[bel[x]] = 0;
}
for (int i = x + 1; i <= rl[bel[x]]; ++i) ++poi1[i];
for (int i = bel[x] + 1; i <= lth; ++i) ++par1[i];
if (par2[bel[x]]) {
for (int i = lr[bel[x]]; i <= rl[bel[x]]; ++i) poi2[i] += par2[bel[x]];
par2[bel[x]] = 0;
}
for (int i = lr[bel[x]]; i <= x - 1; ++i) poi2[i] += x;
for (int i = 1; i <= bel[x] - 1; ++i) par2[i] += x;
}
int main() {
n = read(); m = read();
boc = 316;
for (int i = 1; i <= n; ++i) a[i] = read(), lim = max(lim, a[i]);
for (int i = 1; i <= lim; ++i) bel[i] = (i - 1) / boc + 1;
bel[0] = 1; bel[lim + 1] = lim / boc + 1;
lth = bel[lim + 1];
for (int i = 1; i <= lth; ++i) lr[i] = rl[i - 1] + 1, rl[i] = i * boc;
lr[1] = 0; rl[lth] = lim + 1;
for (int i = 1; i <= m; ++i) {
lt[i] = read(); rt[i] = read(); id[i] = i; blo[i] = lt[i] / boc;
}
sort(id + 1, id + 1 + m, cmp);
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + query2(lim) - query2(a[i]); add2(a[i], a[i]);
pre[i] += (query1(a[i] - 1) + 1) * a[i]; add1(a[i], 1);
}
memset(arr1, 0, sizeof(arr1)); memset(arr2, 0, sizeof(arr2));
for (int i = n; i >= 1; --i) {
suf[i] = suf[i + 1] + query2(lim) - query2(a[i]); add2(a[i], a[i]);
suf[i] += (query1(a[i] - 1) + 1) * a[i]; add1(a[i], 1);
}
int l = 1, r = 0;
for (int i = 1; i <= m; ++i) {
int ql = lt[id[i]], qr = rt[id[i]];
if (r < qr) {
lp[l].push_back((mdzz) {-1, r + 1, qr, id[i]});
ans[id[i]] += (pre[qr] - pre[r]); r = qr;
}
if (r > qr) {
lp[l].push_back((mdzz) {1, qr + 1, r, id[i]});
ans[id[i]] += (pre[qr] - pre[r]); r = qr;
}
if (l < ql) {
rs[r].push_back((mdzz) {1, l, ql - 1, id[i]});
ans[id[i]] += (suf[ql] - suf[l]); l = ql;
}
if (l > ql) {
rs[r].push_back((mdzz) {-1, ql, l - 1, id[i]});
ans[id[i]] += (suf[ql] - suf[l]); l = ql;
}
}
int siz, lq, rq, idt; ll op;
for (int i = 1; i <= n; ++i) { siz = lp[i].size();
for (int j = 0; j < siz; ++j) {
op = lp[i][j].op; idt = lp[i][j].id;
lq = lp[i][j].l; rq = lp[i][j].r;
for (int k = lq; k <= rq; ++k)
ans[idt] += op * (par2[bel[a[k]]] + poi2[a[k]] + a[k] * (par1[bel[a[k]]] + poi1[a[k]]));
}
pushup(a[i]);
}
memset(par1, 0, sizeof(par1)); memset(poi1, 0, sizeof(poi1));
memset(par2, 0, sizeof(par2)); memset(poi2, 0, sizeof(poi2));
for (int i = n; i >= 1; --i) { siz = rs[i].size();
for (int j = 0; j < siz; ++j) {
op = rs[i][j].op; idt = rs[i][j].id;
lq = rs[i][j].l; rq = rs[i][j].r;
for (int k = lq; k <= rq; ++k)
ans[idt] += op * (par2[bel[a[k]]] + poi2[a[k]] + a[k] * (par1[bel[a[k]]] + poi1[a[k]]));
}
pushup(a[i]);
}
for (int i = 1; i <= m; ++i) ans[id[i]] += ans[id[i - 1]];
for (int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}