P3157 [CQOI2011]动态逆序对(CDQ分治)

时间:2019-11-30
本文章向大家介绍P3157 [CQOI2011]动态逆序对(CDQ分治),主要包括P3157 [CQOI2011]动态逆序对(CDQ分治)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目描述

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

输入格式

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

输出格式

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

输入输出样例

输入 #1
5 4
1
5
3
4
2
5
1
4
2
输出 #1
5
2
2
1

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

说明/提示

N<=100000 M<=50000

题解: 对于一个逆序对,加一个时间维,越往后删除的时间戳越小,没有删除的值的时间戳为0,这题就变成了一个三维偏序.

我们把时间看作第一维, 从小到大排序, (j,i)作为一个逆序对出现, 则j的时间戳必定小于i的时间戳.

那么有两种情况, Tj < Ti, Pj < Pi, Vj > Vi        or     Tj < Ti, Pj > Pi, Vj < Vi  ( T代表时间,P代表位置,V代表值)

分情况处理即可.

由于我们处理的是每个时间戳所增加的逆序对,所以我们要求一个前缀和,然后倒序输出即可.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200010;
ll c[maxn];
int n,m;
int lowbit(int x){return x & (-x);}
void add(int pos,int x){
    for(int i = pos;i <= n;i += lowbit(i)){
        c[i] += x;
    }
}
ll query(int pos){
    ll sum = 0;
    for(int i = pos; i >= 1;i -= lowbit(i)){
        sum += c[i];
    }
    return sum;
}
struct node{
    int tim,pos,val;
}a[maxn];
int cmp1(node a,node b){//按照时间排序
    if(a.tim == b.tim)return a.pos < b.pos;
    return a.tim < b.tim;
}
int cmp2(node a,node b){//按照位置从小到大排序
    return a.pos < b.pos;
}
int cmp3(node a,node b){//按照位置从大到小排序
    return a.pos > b.pos;
}
ll pos[maxn],ans[maxn];
void cdq(int l,int r){
    if(l == r)return ;
    int mid = (l + r) / 2;
    cdq(l,mid),cdq(mid+1,r);
    int i = mid + 1,j = l;//这里按照第二维 位置从小到大排序,对于右边区间的每一个点i,查询满足 pos(j) < pos(i) && val(j) > val(i)的j有多少个
    sort(a + l,a + mid + 1,cmp2);
    sort(a + mid + 1,a + r + 1,cmp2);
    for(;i <= r;i++){
        while(a[j].pos <= a[i].pos && j <= mid){
            add(a[j].val,1);
            j++;
        }
        ans[a[i].tim] += query(n) - query(a[i].val);
    }
    for(int i = l;i < j;i++)add(a[i].val,-1);//清空树状数组
    i = mid + 1,j = l;//这里按照第二维 位置从大到小排序,对于右边区间的每一个点i,查询满足 pos(j) > pos(i) && val(j) < val(i)的j有多少个
    sort(a + l,a + mid + 1,cmp3);
    sort(a + mid + 1,a + r + 1,cmp3);
    for(;i <= r;i++){
        while(a[j].pos >= a[i].pos && j <= mid){
            add(a[j].val,1);
            j++;
        }
        ans[a[i].tim] += query(a[i].val);
    }
    for(int i = l;i < j;i++)add(a[i].val,-1);//清空树状数组
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++){
        cin >> a[i].val;
        a[i].tim = 0;
        pos[a[i].val] = i;//记录每个值的位置
        a[i].pos = i;
    }
    for(int i = 1;i <= m;i++){
        int x;
        cin >> x;
        a[pos[x]].tim = m - i + 1;//时间戳更新
    }
    sort(a+1,a+1+n,cmp1);
    cdq(1,n);
    for(int i = 1;i <= m;i++)ans[i] += ans[i-1];
    for(int i = m;i >= 1;i--)cout << ans[i] << endl;
    return 0;
}
View Code

原文地址:https://www.cnblogs.com/cherish-lin/p/11963823.html