问题描述

LG1393


题解

本题可以使用\(\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, \lfloor\frac{l+r}{2} \rfloor]\)\([\lfloor \frac{l+r}{2} \rfloor + 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, \lfloor\frac{l+r}{2} \rfloor],\forall y \in [\lfloor \frac{l+r}{2} \rfloor + 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 ***(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,***);sort(a+mid+1,a+r+1,***);
    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]," \n"[i==1]);
    return 0;
}