NOI模拟(5.19) JSOID2T3 军训列队 (bzoj5319)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/scar_lyw/article/details/80411790

军训列队

题目背景:

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;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页