UVA11525

二分+树状数组,或线段树

本题由于数据量较小 ( n = 50000 ),n(logn)^2的算法可以通过(二分加树状数组,通常解法),此处只讨论nlogn的算法(线段树的妙用)
实际上,这个问题是一个支持单点删除的查询所有数中第k大数的问题。在每次完成查询之后,要把这个数从数的集合中删除。
那么如何用线段树优化掉一个logn?
回想我们在用线段树进行单点查询的时候,其思想就是用的二分查找的思想,具体体现在:
令mid=L+R>>1
如果pos<=mid,那么说明待查找点在前半个区间,否则在后半个区间。我们可以利用这样的思想,借助线段树可以在一个节点中o(1)查询区间L~R的和的特性,判断出我们需要的第p个序号是出现在前半区间还是后半区间,对此进行递归查找,查找结束后删除第p个标号,重新维护每个节点的区间和信息。

//线段树维护实现O(logn)查询,O(logn)删除
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50006;
int tr[maxn << 2], k;
void build(int x, int L, int R)
{
    tr[x] = R - L + 1;
    if (L == R)return;
    int mid = L + R >> 1;
    build(x << 1, L, mid);
    build(x << 1 | 1, mid + 1, R);
}
int query(int x, int L, int R, int p)
{
    if (L == R) {
        tr[x] = 0;
        return L;
    }
    int mid = L + R >> 1;
    int ret = 0;
    if (tr[x << 1] >= p)ret = query(x << 1, L, mid, p);
    else ret = query(x << 1 | 1, mid + 1, R, p - tr[x << 1]);
    tr[x] = tr[x << 1] + tr[x << 1 | 1];
    return ret;
}
int main()
{
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d", &k);
        build(1, 1, k);
        for (int i = 1; i <= k; i++) {
            int x; scanf("%d", &x);
            printf("%d%c", query(1, 1, k, x + 1), i == k ? '\n' : ' ');
        }
    }
}
//常规思路,二分加树状数组
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
int n, k, a[maxn];
int c[maxn], x;
int lowbit(int x)
{
    return x & -x;
}
int sum(int x)
{
    int ret = 0;
    while (x) {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}
void add(int p, int x)
{
    while (p <= k) {
        c[p] += x;
        p += lowbit(p);
    }
}
int main()
{
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d",&k);
        for (int i = 1; i <= k; i++) {
            scanf("%d", &a[i]);
            a[i]++;
        }
        for (int i = 1; i <= k; i++)c[i] = 0;
        for (int i = 1; i <= k; i++)add(i, 1);
        for (int i = 1; i <= k; i++) {
            int L = 1, R = k;
            while (L < R) {
                int mid = L + R >> 1;
                if (sum(mid) >= a[i])R = mid;
                else L = mid + 1;
            }
            add(L, -1);
            printf("%d%c", L, i == k ? '\n' : ' ');
        }
    }
}/

猜你喜欢

转载自blog.csdn.net/CharlieHuu/article/details/81368171