SPOJ TEMPLEQ

题意:

有N个队伍(1 <= N <= 100,000),每个队伍开始有ai个人[0 <= ai<= 100,000,000],有Q个操作[0<=Q<= 500,000]

操作分为三种,1 A:表示在第A个队列加一个人。 2 X:表示求长度大于等于X队列数量。3 Y:表示所有长度大于等于Y的队列减去一个人。

题解:

把各个队列按长度排序

用差分数列来维护这个数组,这样求每个队列的长度就是求前缀和。每次求长度的复杂度是lgn,因为队列是按长度排序的,所以可以通过二分查找到某个长度在队列中的位置,复杂度为lgn*lgn。

两个数组sa[i]记录每个按长度排序后的第i个队列原来的位置。 rk[i]记录在位置i的队列按长度排序的位置。

对于在第i个队列加一个人,求出第i个队列的长度len,在所有长度为len的队列的最后一个加一,这样操作是为了不改变队列的顺序,然后是需要交换下i的位置和队列中长度为len的最后一个位置就好了。

AC代码(950MS):

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
struct Node {
    int pos;
    int len;
    bool operator < (Node x) const {
        return len < x.len;
    }
} a[N];
int sa[N], rk[N], bit[N];
int n, q;

int lowbit(int x) { return x & -x; }

void add(int pos, int val)
{
    while (pos <= n) {
        bit[pos] += val;
        pos += lowbit(pos);
    }
}

int sum(int pos)
{
    int res = 0;
    while (pos) {
        res += bit[pos];
        pos -= lowbit(pos);
    }
    return res;
}

int lb(int x)
{                                           // 找第一个大于等于x的数
    int l = 1, r = n + 1, m;
    while (l < r) {
        m = (l + r) >> 1;
        if (sum(m) < x) l = m + 1;
        else r = m;
    }
    return r;
}

int main()
{
    while (~scanf("%d%d", &n, &q)) {
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i].len);
            a[i].pos = i;
        }
        sort(a + 1, a + 1 + n);
        for (int i = 1; i <= n; ++i) {
            sa[i] = a[i].pos;
            rk[ a[i].pos ] = i;
            add(i, a[i].len - a[i - 1].len);
        }
        int ch, x;
        while (q--) {
            scanf("%d%d", &ch, &x);
            if (ch == 1) {
                int bp = rk[x];             // 原来的位置
                int len = sum(bp);
                int sp = lb(len + 1) - 1;   // 加一后的位置
                swap(rk[ sa[bp] ], rk[ sa[sp] ]);
                swap(sa[bp], sa[sp]);
                add(sp, 1); add(sp + 1, -1);
            } else if (ch == 2) {
                int ans = lb(x);
                printf("%d
", n - ans + 1);
            } else {
                int sp = lb(x);
                add(sp, -1);
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wenruo/p/5452741.html