【CDQ分治】[CQOI2011][NKOJ2041]動態逆序對

題目描述

Description

對於序列A,它的逆序對數定義爲滿足i<j ,且AAi>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 比它小的元素的個數就可以保證但是因爲這裏我們還有第三個條件,那麼我們使用樹狀數組將第三個元素作爲前綴和保存下來,就可以從當前的所有滿足前兩個條件的數量中,減去不滿足第三個條件的就可以完成題目,這裏感謝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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章