UVA11990 ``Dynamic'' Inversion (树状数组套平衡树)

本文探讨了如何高效地计算排列中在每次元素删除前的逆序对数量,利用平衡树结构优化动态逆序对计算过程,提供了解决此类问题的算法实现与性能分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

``Dynamic'' Inversion

3000ms
131072KB
This problem will be judged on UVA. Original ID:  11990
64-bit integer IO format:  %lld      Java class name:  Main
Font Size:   

[PDF Link]

Problem D

"Dynamic" Inversion

You are given a permutation {1,2,3,...,n}. Remove m of them one by one, and output the number of inversion pairs before each removal. The number of inversion pairs of an array A is the number of ordered pairs (i,j) such that i < j and A[i] > A[j].

Input

The input contains several test cases. The first line of each case contains two integers n and m (1<=n<=200,000, 1<=m<=100,000). After that, n lines follow, representing the initial permutation. Then m lines follow, representing the removed integers, in the order of the removals. No integer will be removed twice. The input is terminated by end-of-file (EOF). The size of input file does not exceed 5MB.

Output

For each removal, output the number of inversion pairs before it.

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Output for the Sample Input

5
2
2
1

Explanation

(1,5,3,4,2)->(1,3,4,2)->(3,4,2)->(3,2)->(3)


Rujia Liu's Present 3: A Data Structure Contest Celebrating the 100th Anniversary of Tsinghua University
Special Thanks: Yi Chen, Dun Liang
Note: Please make sure to test your program with the gift I/O files before submitting!
给你一个1到n的排列,进行m次删除操作,输出每次删除之前的逆序对数,静态逆序对可以用归并排序或者树状数组来求,动态的可以用树状数组套平衡树来求,具体来说,就是树状数组的每个节点是一颗平衡树,第x个节点保存x,x-1,x-2...,x-lowbit(x)+1的所有值(建成平衡树),假设要删除的数是x[i]在原排列中的位置是pos[i],我们只需要知道1到pos[i-1]中有多少个数比x[i]大和pos[i+1]到结尾有多少个数比x[i]小,就能知道逆序对减少了多少(已经删除的点直接忽略掉). 那么我们可以通过在pos[i],pos[i]-lowbit(pos[i]),pos[i]-lowbit(pos[i]) - lowbit( pos[i]-lowbit(pos[i]))...中查找有多少个数比小于等于x,记为t2,则1到pos[i]中节点总数减t2就得到前缀比x[i]大的,小于等于x的总数(可以通过树状数组查询 ask(c,val) )-t2就是后缀里比x小的,然后更新pos[i],pos[i]+lowbit(pos[i]).....这些平衡树,给被删除的节点打上标记(--siz即可),同时在原树状数组里x[i]位置上加上-1.


#include <bits/stdc++.h>
int lowbit(int x) {return x & -x;}
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int M = 4e6 + 10;
int tot;
struct Splay {
    int fa,key,siz;
    int ch[2];
    Splay() = default;
    Splay(int fa,int key):fa(fa),key(key),siz(1){
        ch[0] = ch[1] = 0;
    }
}Node[M];
int newNode(int fa,int key)
{
    int rt = ++tot;
    Node[rt] = Splay(fa,key);
    return rt;
}
int built(vector<int>&v,int fa,int l,int r)
{
    int mid = (l + r) >> 1;
    int rt = newNode(fa,v[mid-1]);
    Splay & t = Node[rt];
    if(mid > l) t.ch[0] = built(v,rt,l,mid-1);
    if(mid < r) t.ch[1] = built(v,rt,mid+1,r);
    t.siz += Node[t.ch[0]].siz + Node[t.ch[1]].siz;
    return rt;
}
int ask(int rt,int x)///询问rt为根的子树里有多少不大于x的节点
{
    if(!rt)return 0;
    Splay & t = Node[rt];
    if(t.key == x) {
        return Node[t.ch[0]].siz+1;
    }
    bool flag = (t.siz - (Node[t.ch[0]].siz + Node[t.ch[1]].siz));///是否没被删除
    if(t.key < x) return Node[t.ch[0]].siz + flag + ask(t.ch[1],x);
    return ask(t.ch[0],x);
}
void remove(int rt,int x)
{
    Splay & t = Node[rt];
    --t.siz;
    if(t.key==x)return;
    remove(t.ch[t.key < x],x);
}
int n,m;
int root[N],pos[N];
vector<int>vec[N];
int c[N];
void add(int *T,int x,int d)
{
    for(;x <= n; x += (x&-x)) {
        T[x] += d;
    }
}
int ask(int *T,int x)
{
    int ans = 0;
    for(;x > 0; x -= (x&-x)) {
        ans += T[x];
    }
    return ans;
}
ll cnt;
int init()
{
    if(scanf("%d%d",&n,&m)!=2) return 0;
    memset(c,0,sizeof(int)*(n+1));
    for(int i = 0; i <= n; ++i) vec[i].clear();
    tot = cnt = 0;
    Node[0].siz = 0;
    for(int i = 1; i <= n; ++i) {
        int x;
        scanf("%d",&x);
        cnt += (i-1)-ask(c,x);
        add(c,x,1);
        pos[x] = i;
        for(int j = i; j <= n; j += (j&-j)) {
            vec[j].push_back(x);
        }
    }
    for(int i = 1; i <= n; ++i) {///可以通过打标记将删除变成静态的,直接按照完全二叉树来建树即可
        sort(vec[i].begin(),vec[i].end());
        root[i] = built(vec[i],0,1,vec[i].size());
    }
    return 1;
}
void change(int x,int val)
{
    int t1 = 0,t2 = 0;
    for(; x > 0; x -= (x&-x)) {
        int t = ask(root[x],val); //查询root[x]里有多少个数小于等于val
        t2 += t;
        t1 += Node[root[x]].siz - t;//大于val的个数
    }
    t2 = ask(c,val) - t2;//后缀里小于val的个数
    add(c,val,-1);//在val位置加-1
    cnt -= (t1+t2);//删除之后对逆序对的影响
    x = pos[val];
    for( ;x <= n; x += (x&-x)) remove(root[x],val);//在受影响的平衡树中删除节点
}
int main()
{
    while(init()) {
        for(int i = 0; i < m; ++i) {
            int x;scanf("%d",&x);
            printf("%lld\n",cnt);
            change(pos[x],x);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值