【CDQ分治】[CQOI2011][NKOJ2041]动态逆序对

题目描述

Description

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

Input

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

Output

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

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1
Hint

样例解释

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

数据范围

编号 1-2 3-4 5-6 7-8 9-10
n <=1000 <=30000 <=50000 <=60000 <=100000
m <=100 <=10000 <=20000 <=40000 <=50000

题目分析

首先我们可以发现对于每一个插入操作我们可以看作 (t,x,y) 的三元组也就是时间、位置、数值,只有当对于两个组出现 t0<t,x0<x,y0>y 或者 t0<t,x0>x,y0<y 的数对的数量,那么我们可以使用CDQ分治来搞一搞。首先可以发现我们的 t,x,y 每一个t都不存在另一个t’=t那么因为我们要保证的是每一个 t0<t 那么我们将 t 作为划分的关键字,在归并排序之前我们先将x作为排序的关键字,那么在划分之后我们可以保证划分后的左边和右边的 x 仍保持升序,同时保证左边的所有t都小于右边的,那么我们对于右边的每一个只需要找左边的所有的 x <script type="math/tex" id="MathJax-Element-868">x</script>比它小的元素的个数就可以保证但是因为这里我们还有第三个条件,那么我们使用树状数组将第三个元素作为前缀和保存下来,就可以从当前的所有满足前两个条件的数量中,减去不满足第三个条件的就可以完成题目,这里感谢azui大神的帮助这里写图片描述
http://blog.csdn.net/u011542204/article/details/50571409

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 100000;
const int MAXM = 50000;
long long Tree[MAXN+10], up[MAXN+10];
int vtopos[MAXN+10], A[MAXN+10], n, m, time, Del[MAXM+10];
struct Pair{
    int a, b, c;
    Pair():a(0),b(0),c(0){}
    Pair(int _a, int _b, int _c):a(_a),b(_b),c(_c){}
}Tasks[MAXN+10], tmp[MAXN+10];
int lowbit(int u){return (u&(-u));}
void add(int u, int v){
    for(;u<=n;u+=lowbit(u)) Tree[u]+=1LL * v;
}
long long que(int u){
    long long ret = 0;
    for(;u;u-=lowbit(u)) ret += Tree[u];
    return ret;
}
void make_task(int k){
    int pos = vtopos[k];
    ++time;
    Tasks[time] = Pair(time, pos, k);
}
void cdq(int L, int R){
    if(R - L <= 0) return ;
    int mid = (L + R) >> 1;
    int l = L, r = mid+1, Len = R-L+1;
    for(int i=0;i<Len;i++){
        if(Tasks[i+L].a <= mid) tmp[l++] = Tasks[i+L];
        else tmp[r++] = Tasks[i+L];
    }
    for(int i=L; i<=R; i++) Tasks[i] = tmp[i];
    l = L;
    for(int i=mid+1; i<=R; i++){
        for(;l<=mid&&Tasks[i].b > Tasks[l].b;l++) add(Tasks[l].c, 1);
        up[Tasks[i].a] += l-L-que(Tasks[i].c);
    }
    l--;
    for(;l>=L;l--) add(Tasks[l].c, -1);
    l = mid;
    for(int i=R; i>mid; i--){
        for(;l>=L&&Tasks[l].b>Tasks[i].b;l--) add(Tasks[l].c, 1);
        up[Tasks[i].a] += que(Tasks[i].c);
    }
    l++;
    for(;l<=mid;l++) add(Tasks[l].c, -1);
    cdq(L, mid); cdq(mid+1, R);
}
bool cmp(Pair a, Pair b){return a.b < b.b;}
int main(){
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++){
        scanf("%d", &A[i]);
        vtopos[A[i]] = i;
    }
    for(int i=1;i<=m;i++) scanf("%d", &Del[i]);
    for(int i=m;i>=1;i--) A[vtopos[Del[i]]] = -1;
    for(int i=1;i<=n;i++) if(A[i] != -1) make_task(A[i]);
    for(int i=m;i>=1;i--) make_task(Del[i]);
    sort(Tasks+1, Tasks+1+n, cmp);
    cdq(1, n);
    for(int i=2;i<=n;i++) up[i] += up[i-1];
    for(int i=1;i<=m;i++) printf("%I64d\n", up[n-i+1]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值