Luogu P3157 [CQOI2011]动态逆序对

题目链接 \(Click\) \(Here\)

这个题有点卡常数。。我的常数比较大所以是吸着氧气跑过去的。。。

题意:计算对于序列中每个位置\(p\)\([1,p-1]\)区间内比它大的数的个数,和\([p + 1, N]\)区间内比它小的数的个数和,要求支持修改操作,带修主席树可以解决。

通过主席树来维护权值状态和比某个数大/小的数的个数,用树状数组来支持修改和维护一个主席树的前缀和(主席树前缀和具有可减性)。时间空间\(O(Nlog^2N)\)\(1000ms+128MB\)的限制对本算法略为苛刻,但卡常可过。

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

const int N = 100010;
#define mid ((l + r) >> 1)
#define lowbit(x) (x & -x)

int n, m, tot, rt[N], arr[N], pos[N];

struct Segment_Node {
    int sz, ls, rs;
}t[N << 8];

void modify (int &_rt, int l, int r, int w, int del) {
    if (_rt == 0) _rt = ++tot;
    t[_rt].sz += del;
    if (l != r) {
        if (w <= mid) {
            modify (t[_rt].ls, l, mid, w, del);
        } else {
            modify (t[_rt].rs, mid + 1, r, w, del);
        }
    }
}


namespace rev {
    int a[N];

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

    inline int get_sum (int pos) {
        register int res = 0;
        while (pos) {
            res += a[pos];
            pos -= lowbit (pos);
        }
        return res;
    }
    
    long long get_rev () {
        long long res = 0;
        for (int i = n; i >= 1;--i) {
            res += get_sum (arr[i]);
            add (arr[i], 1);
        }
        return res;
    }
}

int _query (int _rt, int l, int r, int nl, int nr) {
    if (nl <= l && r <= nr) return t[_rt].sz;
    register int res = 0;
    if (nl <= mid) res += _query (t[_rt].ls, l, mid, nl, nr);
    if (mid < nr) res += _query (t[_rt].rs, mid + 1, r, nl, nr);
    return res;
}

inline int query (int l, int r, int w, int type) {
    l = l - 1;
    //求序列里面[l, r]内有多少数大于w (type = 1)
    //求序列里面[l, r]内有多少数小于w (type = 2)
    // printf ("l = %d, r = %d, w = %d, type = %d\n", l, r, w, type);
    register int i, res = 0;
    for (i = l; i != 0; i -= lowbit (i)) {
        if (type == 1) res -= _query (rt[i], 0, n + 1, w + 1, n);
        if (type == 2) res -= _query (rt[i], 0, n + 1, 1, w - 1);
    }
    // printf ("res = %d\n", res);
    for (i = r; i != 0; i -= lowbit (i)) {
        if (type == 1) res += _query (rt[i], 0, n + 1, w + 1, n);
        if (type == 2) res += _query (rt[i], 0, n + 1, 1, w - 1);
    }
    // printf ("l = %d, r = %d, w = %d, type = %d, res = %d\n", l, r, w, type, res);
    return res;
}

inline int read () {
    int s = 0, w = 1, ch = getchar ();
    while ('9' < ch || ch < '0') {
        ch = getchar ();
    }
    while ('0' <= ch && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * w;
}

int main () {
    n = read (), m = read ();
    register int i, j, w;
    for (i = 1; i <= n; ++i) {
        arr[i] = read ();
        pos[arr[i]] = i;
        for (j = i; j <= n; j += lowbit (j)) {
            modify (rt[j], 0, n + 1, arr[i], +1);
        }
    }
    long long ans = rev :: get_rev ();
    for (i = 1, w = 0; i <= m; ++i) {
        printf ("%lld\n", ans);
        w = read ();
        ans -= query (1, pos[w] - 1, w, 1);
        ans -= query (pos[w] + 1, n, w, 2);
        for (j = pos[w]; j <= n; j += lowbit (j)) {
            modify (rt[j], 0, n + 1, w, -1);
        }
    }
}

转载于:https://www.cnblogs.com/maomao9173/p/10535560.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值