動態逆序對[CDQ分治]
阿新 • • 發佈:2018-12-16
題目描述
對於序列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; }