Uva 11990 "Dynamic" Inversion(树状数组 + 数据结构分块)

题意:给一个1 ~ n的排列,有m次删除操作,问每次删除前该序列有多少逆序对


思路:预处理原来总共有多少逆序对,树状数组即可,求出以每个数为逆序对有多少个,删除的时候,先用剩余的逆序对数减去以该数形成的逆序对数,然后看删除的数有多少与该数形成逆序对,再加回去,可以使用分块的方法,快速查询有多少删除的数与其形成逆序对,每次删除的数加在一个新数组上,对新数组分块查找就行了


#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
const int maxn = 2 * 1e5 + 10;
const int sz = 2345;
using namespace std;

int block[maxn / sz + 1][sz];
int a[maxn], id[maxn];
int inv[maxn];
int C[maxn];
int n, m, d;

int query(int l, int r, int v, int op) {
    if(l > r) return 0;
    int lb = l / sz, rb = r / sz;
    int k1 = 0, k2 = 0;
    if(lb == rb) {
        for(int i = l; i <= r; i++) {
            if(a[i] && a[i] < v) k1++;
            if(a[i] > v) k2++;
        }
    } else {
        for(int i = l; i < (lb + 1) * sz; i++) {
            if(a[i] && a[i] < v) k1++;
            if(a[i] > v) k2++;
        }
        for(int i = rb * sz; i <= r; i++) {
            if(a[i] && a[i] < v) k1++;
            if(a[i] > v) k2++;
        }

        for(int b = lb + 1; b < rb; b++) {
            int c1 = lower_bound(block[b], block[b] + sz, v) - block[b];
            int c2 = upper_bound(block[b], block[b] + sz, 0) - lower_bound(block[b], block[b] + sz, 0);
            k1 += c1 - c2;
            int ind = upper_bound(block[b], block[b] + sz, v) - block[b];
            k2 += sz - ind;
        }
    }
    if(op == 1) return k1;
    if(op == 2) return k2;
}

void update(int p, int x) {
    if(a[p] == x) return ;
    int old = a[p], pos = 0, *B = &block[p / sz][0];
    a[p] = x;
    while(B[pos] < old) pos++; B[pos] = x;
    if(x > old) {
        while(pos < sz - 1 && B[pos] > B[pos + 1]) {
            swap(B[pos], B[pos + 1]); pos++;
        }
    } else {
        while(pos && B[pos] < B[pos - 1]) {
            swap(B[pos], B[pos - 1]); pos--;
        }
    }
}

int sum(int *C, int i) {
    int c = 0;
    while(i) {
        c += C[i];
        i -= i & -i;
    }
    return c;
}

void update(int *C, int i) {
    while(i <= n) {
        C[i]++;
        i += i & -i;
    }
}

int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        memset(inv, 0, sizeof inv);
        memset(C, 0, sizeof C);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            id[a[i]] = i;
        }
        ll tal = 0;
        for(int i = n; i >= 1; i--) {
            int g = sum(C, a[i] - 1);
            tal += g;
            inv[i] += g;
            inv[i] += i + g - a[i];
            update(C, a[i]);
        }
        memset(a, 0, sizeof a);
        while(m--) {
            printf("%lld\n",tal);
            scanf("%d", &d);
            tal -= inv[id[d]];
            tal += query(id[d] + 1, n, d, 1);
            tal += query(1, id[d] - 1, d, 2);
            update(id[d], d);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值