1. 程式人生 > >動態逆序對[CDQ分治]

動態逆序對[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

分析

將刪除看做倒著插入,記錄三元組{time,pos,val}

time表示第幾個插入,很明顯第一個刪除的time為n

pos表示在原佇列中的位置

val表示值

我們按time排序,發現對當前time的答案有貢獻的為

time_i<time

pos_i>pos && val_i<val

或者 pos_i<pos && val_i>val

於是就是3維偏序問題了

#include<bits/stdc++.h>
#define N 200005
#define LL long long
using namespace std;
struct Node{int t,x,y;}q[N],tmp[N];
int n,m,Time,a[N],match[N],c[N];
LL ans[N];
bool cmp(Node a,Node b){return a.t<b.t;}
int read(){
	int cnt=0;char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt;
}
void add(int x,int val){for(;x<=n;x+=x&-x) c[x]+=val;}
int quary(int x){
	int ans=0;
	for(;x;x-=x&-x) ans+=c[x];
	return ans;
}
void CDQ(int l,int r){
	if(l==r) return;
	int mid=(l+r)>>1;
	CDQ(l,mid),CDQ(mid+1,r);
	int i=l,j=mid+1;
    //處理pos_i<pos 且 val_i>val的
	for(int k=l;k<=r;k++){
		if(j>r||(i<=mid&&q[i].x<q[j].x)) add(q[i].y,1),tmp[k]=q[i++];
		else ans[q[j].t]+=quary(n)-quary(q[j].y),tmp[k]=q[j++];
	}
	for(int k=l;k<=mid;k++) add(q[k].y,-1);
	for(int k=l;k<=r;k++) q[k]=tmp[k];
    //處理pos_i>pos && val_i<val 的貢獻 (已經按x排好序)
	for(int i=r;i>=l;i--){
		if(q[i].t<=mid) add(q[i].y,1);
		else ans[q[i].t]+=quary(q[i].y); 
	}
	for(int i=l;i<=r;i++)
		if(q[i].t<=mid) add(q[i].y,-1);
}
int main(){
	n=read(),m=read(); Time=n;
	for(int i=1;i<=n;i++){
		a[i]=read();
		q[i].t=0 , q[i].x=i , q[i].y=a[i];
		match[q[i].y]=i;
	}
	for(int i=1;i<=m;i++){
		q[match[read()]].t=Time--;
	}
	for(int i=1;i<=n;i++)
		if(q[i].t==0) q[i].t=Time--;
	sort(q+1,q+n+1,cmp);
	CDQ(1,n);
	for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
	for(int i=n;i>=n-m+1;i--) printf("%lld\n",ans[i]);
	return 0;
}