军训列队
题目背景:
5.19 模拟 JSOI2018D2T3
分析:二分 + 主席树
没有想到啊,最水的题竟然在T3,考虑显然如果按照原本的相对顺序填入集合区间,所得的代价一定是最优越的,那么也就是说,左边的一部分人要往右边跑,右边的一部分人会往左边跑,那么只要找到这个左右分界处,随便求几个区间和然后加加减减就可以了,考虑如何找边界,我们考虑在主席树上二分,找到最左边的满足从这个位置到k + r - l中的空位与这个位置之后的编号[l, r]之间的人数相等的位置,那么从这里分成左右两界计算一下就可以了。
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
///*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
if (c == '-') iosig = true;
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int MAXN = 500000 + 10;
const int MAXX = 2000000;
struct node {
int cnt, left, right;
long long sum;
} tree[MAXN * 23];
int cnt, n, m, l, r, k;
int root[MAXN], a[MAXN];
inline void insert(int &cur, int l, int r, int pos) {
tree[++cnt] = tree[cur], cur = cnt;
tree[cur].cnt++, tree[cur].sum += (long long)pos;
if (l == r) return ;
int mid = l + r >> 1;
if (pos <= mid) insert(tree[cur].left, l, mid, pos);
else insert(tree[cur].right, mid + 1, r, pos);
}
inline long long sum(int l, int r) {
return (long long)(l + r) * (r - l + 1) / 2LL;
}
inline void solve(int ql, int qr, int pl) {
int pr = pl + qr - ql, l = 1, r = MAXX, x = root[ql - 1], y = root[qr];
long long l_sum = 0, r_sum = 0;
int size = 0;
while (l != r) {
int mid = l + r >> 1;
int cur_l = tree[tree[y].left].cnt - tree[tree[x].left].cnt;
if (cur_l + size >= mid - pl + 1) {
l = mid + 1, size += cur_l;
l_sum += tree[tree[y].left].sum - tree[tree[x].left].sum;
x = tree[x].right, y = tree[y].right;
} else r = mid, x = tree[x].left, y = tree[y].left;
}
r_sum = tree[root[qr]].sum - tree[root[ql - 1]].sum - l_sum;
W(sum(pl, l - 1) - l_sum + r_sum - sum(l, pr)), write_char('\n');
}
inline void solve() {
R(n), R(m), root[0] = 0;
for (int i = 1; i <= n; ++i)
root[i] = root[i - 1], R(a[i]), insert(root[i], 1, MAXX, a[i]);
while (m--) R(l), R(r), R(k), solve(l, r, k);
}
int main() {
//freopen("line.in", "r", stdin);
//freopen("line.out", "w", stdout);
solve();
flush();
return 0;
}