题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3141
题目大意:
给出一个1-n的排列A,要求按照某种顺序删除一些数(其他数顺序不变),输出每次删除之前逆序对的数目。
解题思路:
动态逆序对,在线的话用块状数组、树套树等高级数据结构解决,然此题可离线,于是可用cdq分治这种淫技奇巧代替。
先用树状数组求出序列原本的逆序对数,去掉某一个数就要减去前面比它大的数的个数以及后面比它小的数的个数,但是去掉一个数后会对其后面的操作产生影响,所以需要计算每次去掉一个数之前去掉的数中,在该数前比该数大的以及在该数后比该数小的,这样操作间构成了一种三维的偏序关系,cdq分治解之。
AC代码:
import java.util.*;
public class Main {
static int n,m;
static long ans;
static int[] aa=new int[200005];
static int[] bb=new int[200005];
static int[] cc=new int[100005];
static int[] dd=new int[100005];
static int[] mm=new int[200005];
static int[] bit=new int[200005];
static Pair[] pp=new Pair[100005];
static void add(int i,int x)
{
for(;i<=n;i+=i&-i)
bit[i]+=x;
}
static int sum(int i)
{
int res=0;
for(;i>0;i-=i&-i)
res+=bit[i];
return res;
}
static void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)/2;
cdq(l,mid);
for(int i=l;i<=r;i++)
pp[i]=new Pair(cc[i],mm[cc[i]],i);
work(l,r);
for(int i=l;i<=r;i++)
pp[i]=new Pair(mm[cc[i]],cc[i],i);
work(l,r);
cdq(mid+1,r);
}
static void work(int l,int r)
{
int mid=(l+r)/2;
Arrays.sort(pp,l,r+1);
for(int i=l;i<=r;i++)
{
if(pp[i].z<=mid)
add(pp[i].y,1);
else
dd[pp[i].z]+=sum(pp[i].y);
}
for(int i=l;i<=r;i++)
if(pp[i].z<=mid) add(pp[i].y,-1);
}
static class Pair implements Comparable<Pair>
{
int x,y,z;
Pair(int a,int b,int c)
{
x=a;y=b;z=c;
}
public int compareTo(Pair p) {
return p.x-x;
}
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
while(in.hasNext())
{
n=in.nextInt();m=in.nextInt();
for(int i=1;i<=n;i++)
{
aa[i]=in.nextInt();
mm[aa[i]]=i;
}
ans=0;
for(int i=n;i>0;i--)
{
bb[i]=sum(aa[i]);
ans+=bb[i];
bb[i]+=i+bb[i]-aa[i];
add(aa[i],1);
}
Arrays.fill(bit,0);
for(int i=1;i<=m;i++)
cc[i]=in.nextInt();
Arrays.fill(dd,0);
cdq(1,m);
for(int i=1;i<=m;i++)
{
System.out.println(ans);
ans-=bb[mm[cc[i]]]-dd[i];
}
}
}
}