问题描述
题解
本题可以使用(mathrm{CDQ})分治完成。
二维偏序
根据偏序的定义,逆序对是一个二维偏序,但这个二维偏序比较特殊:
-
(i>j,a_i<a_j)
-
(i<j,a_i>a_j)
以上两种情况都符合这个二维偏序。
三维偏序
但带修改二维偏序怎么做?
我们将删除操作视为插入操作。
则没有没删除的插入时间为(1),第(i)个被删除的,插入时间为(m-i+2)
将插入时间作为第一维,数列位置为第二维,数值为第三维。
做三维偏序即可。
注意由于逆序对的定义,三维偏序仍是两种情况。
(mathrm{CDQ})分治
简要介绍一下(mathrm{CDQ})分治。
(mathrm{CDQ})分治是一种分治算法(废话),是一个叫做陈丹琪的女选手在(2009)(记不清了)年提出的。当时提出时是用于优化一类(mathrm{DP})问题的。后来(mathrm{CDQ})分治被主要用于解决三维偏序问题。
(mathrm{CDQ})分治思想类似于归并排序。归并排序每次将区间([l,r])划分为([l, lfloorfrac{l+r}{2} floor])和([lfloor frac{l+r}{2} floor + 1,r])
考虑三维偏序问题模型,(i)对(j)有贡献需要满足(a_i<a_j,b_i<b_j,c_i<c_j)。
(mathrm{CDQ})分治先将(a)升序排序,再进行归并排序,使(b)有序。
考虑区间([l,r]),当完成左右区间的归并排序后,由于一开始先对(a)进行了排序,则(forall x in[l, lfloorfrac{l+r}{2} floor],forall y in [lfloor frac{l+r}{2} floor + 1,r]),一定有(a_x<a_y),这时候分别维护左区间和右区间指针,同时用树状数组维护值即可。
具体请见【模板】三维偏序(陌上花开)题解区。
(mathrm{code})
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,tmp;
struct node{
int a,b,c,ans;
}a[40007];
int anss[40007];
template<typename Tp>
void read(Tp &x){
x=0;char ch=1;int fh;
while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
if(ch=='-'){
ch=getchar();fh=-1;
}
else fh=1;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
x*=fh;
}
struct BIT{
int c[40007];
void change(int p,int k){
while(p<=40000){
c[p]+=k;p+=(p&(-p));
}
}
int query(int p){
int re=0;
while(p){
re+=c[p];p-=(p&(-p));
}
return re;
}
}T;
bool comp(node a,node b){
if(a.a!=b.a) return a.a<b.a;
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
bool cmp(node a,node b){
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
bool fuck(node a,node b){
if(a.b!=b.b) return a.b>b.b;
return a.c>b.c;
}
void cdq(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
cdq(l,mid);cdq(mid+1,r);
sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
int i,j=l,tot=0;
for(i=mid+1;i<=r;i++){
while(j<=mid&&a[j].b<=a[i].b){
T.change(a[j].c,1);++j;++tot;
}
a[i].ans+=tot-T.query(a[i].c);
}
for(i=l;i<j;i++) T.change(a[i].c,-1);
sort(a+l,a+mid+1,fuck);sort(a+mid+1,a+r+1,fuck);
for(i=mid+1,j=l;i<=r;i++){
while(j<=mid&&a[j].b>=a[i].b){
T.change(a[j].c,1);++j;
}
a[i].ans+=T.query(a[i].c);
}
for(i=l;i<j;i++) T.change(a[i].c,-1);
}
bool fake(node a,node b){
return a.c<b.c;
}
void lsh(){
sort(a+1,a+n+1,fake);
for(register int i=1;i<=n;i++) a[i].c=i;
}
signed main(){
read(n);read(m);
for(register int i=1;i<=n;i++){
read(a[i].c);a[i].a=1,a[i].b=i;
}
for(register int i=1;i<=m;i++){
read(tmp);a[tmp].a=m-i+2;
}
lsh();
sort(a+1,a+n+1,comp);
cdq(1,n);
for(register int i=1;i<=n;i++) anss[a[i].a]+=a[i].ans;
for(int i=1;i<=n;i++)anss[i]+=anss[i-1];
for(int i=m+1;i>0;i--)printf("%lld%c",anss[i],"
"[i==1]);
return 0;
}