题解 P4559 【[JSOI2018]列队】
题意简述:
有
有
每个学生的初始位置互不相同,最终到达的位置也必须互不相同。
题解:
不难证明,学生跑到最终的位置时,他们的相对位置不改变至少是最优解之一,这可以脑补一下。
所以我们只需要求最终相对位置不变时的答案即可。
因为学生两两位置不同,所以最终有一部分学生向右跑,有一部分学生向左跑。
向右跑的学生对答案的贡献是
向左跑的学生对答案的贡献是
显然左边一部分学生向右跑,右边一部分学生向左跑。
考虑使用主席树处理这个问题。
对权值线段树进行可持久化,则编号区间内的学生就是两个线段树相减。
考虑递归进一个区间
-
这个区间中没有学生。直接返回
0 。 -
这个区间中的学生全部往右跑。返回
(\sum k+rk_i-1)-(\sum a_i) ,左边是等差数列求和的形式,右边可以直接记。 -
这个区间中的学生全部往左跑。返回
(\sum a_i)-(\sum k+rk_i-1) 。 -
不能确定这个区间中的学生的方向,递归到子树处理。
直接在主席树上实现即可。
时间复杂度
#include <cstdio>
typedef long long LL;
const int MN = 500005;
const int MS = 11000005;
int n, m, s = 1000000;
int rt[MN];
int ls[MS], rs[MS], mx[MS], mn[MS], sz[MS], cnt;
LL sum[MS];
void Add(int &rt, int l, int r, int p) {
ls[++cnt] = ls[rt], rs[cnt] = rs[rt], sz[cnt] = sz[rt] + 1, sum[cnt] = sum[rt] + p, rt = cnt;
if (l == r) return ;
int mid = l + r >> 1;
if (p <= mid) Add(ls[rt], l, mid, p);
else Add(rs[rt], mid + 1, r, p);
}
LL Qur(int rt1, int rt2, int l, int r, int f, int k) {
if (!(sz[rt1] - sz[rt2])) return 0;
LL Sz = sz[rt1] - sz[rt2], Sum = sum[rt1] - sum[rt2];
if (l >= k + f) return Sum - (2*k + 2*f + Sz - 1) * Sz / 2;
if (r <= k + f + Sz - 1) return (2*k + 2*f + Sz - 1) * Sz / 2 - Sum;
int mid = l + r >> 1, lsz = sz[ls[rt1]] - sz[ls[rt2]];
return Qur(ls[rt1], ls[rt2], l, mid, f, k) + Qur(rs[rt1], rs[rt2], mid + 1, r, f + lsz, k);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, x; i <= n; ++i) {
scanf("%d", &x);
Add(rt[i] = rt[i - 1], 1, s, x);
}
for (int i = 1, l, r, k; i <= m; ++i) {
scanf("%d%d%d", &l, &r, &k);
printf("%lld\n", Qur(rt[r], rt[l - 1], 1, s, 0, k));
}
return 0;
}