COGS-1715 [CQOI2011]动态逆序对(树状数组+线段树)

1715. [CQOI2011]动态逆序对

★★★   输入文件: inverse.in   输出文件: inverse.out    简单对比
时间限制:2 s   内存限制:128 MB

【题目描述】

对于序列A,它的逆序对数定义为满足 i< j,且A i>A j的数对( i, j)的个数。给1到 n的一个排列,按照某种顺序依次删除 m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

【输入格式】

输入第一行包含两个整数 nm,即初始元素的个数和删除的元素个数。以下 n行每行包含一个1到 n之间的正整数,即初始排列。以下 m行每行一个正整数,依次为每次删除的元素。
 

【输出格式】

 
输出包含 m行,依次为删除每个元素之前,逆序对的个数。

【样例输入】

5 4  1  5  3  4  2  5  1  4  2  

【样例输出】

5  2  2  1    样例解释  (1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。  

【提示】


N<=100000 M<=50000

题解:树状数组+线段树

首先用树状数组求一下原始的逆序对,每次删除一个数x的时候,ans-=x后面比x小的数+x前面比x大的数

当然这样算会有重复,因为有些数有可能已经删去了,因此可以用一个二维变量(i,a[i])记录已经删去的值,用树状数组+线段树维护一下即可。时间复杂度O(nlog^2n)

(其实可以用CDQ分治来写,,,不过还是树写起来爽

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MX = 1e5 + 5;
const int MXM = MX * 80;
int n, m, cnt;
int sum[MXM], ls[MXM], rs[MXM];
int a[MX], fa[MX];
LL f[MX];
struct Tree {
    int n;
    vector <LL> T;
    void init(int _n) {
        T.clear();
        n = _n;
        T.resize(n + 1);
    }
    void add(int x, LL v) {
        for (int i = x; i <= n; i += i & -i) T[i] += v;
    }
    LL sum(int x) {
        if (x > n) x = n;
        LL ret = 0;
        for (int i = x; i > 0; i -= i & -i) ret += T[i];
        return ret;
    }
} T;
void update(int p, int val, int l, int r, int prt, int &rt) {
    rt = ++cnt;
    ls[rt] = ls[prt]; rs[rt] = rs[prt];
    sum[rt] = sum[prt] + 1;
    if (l == r) return;
    int m = (l + r) >> 1;
    if (p <= m) update(p, val, l, m, ls[prt], ls[rt]);
    else update(p, val, m + 1, r, rs[prt], rs[rt]);
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return sum[rt];
    int m = (l + r) >> 1, ret = 0;
    if (L <= m) ret += query(L, R, l, m, ls[rt]);
    if (R > m) ret += query(L, R, m + 1, r, rs[rt]);
    return ret;
}
struct BTree {
    int n;
    vector <int> T;
    void init(int _n) {
        T.clear();
        n = _n;
        T.resize(n + 1);
    }
    void add(int x, int y, int v) {
        for (int i = x; i <= n; i += i & -i) update(y, v, 1, n, T[i], T[i]);
    }
    int sum(int x, int l, int r) {
        if (x > n) x = n;
        int ret = 0;
        for (int i = x; i > 0; i -= i & -i) ret += query(l, r, 1, n, T[i]);
        return ret;
    }
} bt;

int main() {
    //freopen("in.txt", "r", stdin);
    freopen("inverse.in", "r", stdin);
    freopen("inverse.out", "w+", stdout);
    scanf("%d%d", &n, &m);
    T.init(n);
    bt.init(n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), fa[a[i]] = i;
    LL ans = 0;
    for (int i = n; i > 0; i--) {
        f[i] = T.sum(a[i]);
        ans += f[i];
        T.add(a[i], 1);
    }
    for (int i = 1, x; i <= m; i++) {
        printf("%lld\n", ans);
        scanf("%d", &x);
        int index = fa[x];
        ans -= 2 * f[index] + index - x;
        ans += bt.sum(index, x, n);
        ans += bt.sum(n, 1, x) - bt.sum(index, 1, x);
        bt.add(index, x, 1);
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值