Description
对于序列A,它的逆序对数定义为满足i < j,且Ai > Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
Sample Output
5
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。
HINT
N ≤ 100000 M ≤ 50000
Source
cdq分治过这题好简单啊www
据说主席树会被卡常数cdq完全不担心
用树状数组预处理出初始序列中逆序对个数,包括每一位的数对应的逆序对个数.
然后开一个数组叫new_inv,用来记录已经被删除的数构成的序列的逆序对个数(因为如果直接在ans里减掉每个数的逆序对,有一些逆序对会被重复计算进去)
然后就开始裸cdq分操作就可以了.
顺便其实这是我第一次用树状数组求逆序对.刚学的.仰慕PoPoQQQ神犇>ω<从他那里get了简单的树状数组函数的写法(因为我才刚会用树状数组两三天→_←别问我为什么才刚学会树状数组因为我是沙茶233)
关于树状数组求逆序对:
其实就相当于把那些数逐个插入进树状数组,看看插入进去的时候比它小的数有多少.
具体的可以看这篇文章
//AC code by CreationAugust
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 101000
#define lbt(x) (x&-x)//扯淡的算符优先级= =不加()就会bomb
using namespace std;
int n,m;
long long ans;
int tot;
int a[MAXN],b[MAXN];//用于树状数组统计逆序对
int new_inv[MAXN];//表示删除某个元素后,已经删除的元素构成的序列里的逆序对数目
int tim[MAXN];
struct Query
{
int num,pos;//num为删除的数,pos为num出现的位置,x为操作出现时间
int x;
bool operator <(const Query& a)const{
return pos<a.pos;
}
}ques[MAXN],newq[MAXN];
int c[MAXN];
int num[MAXN];
void modify(int i,int flag)//这种写法把向下修改和向上修改写到一起
{
for (;i>0&&i<=n;i+=lbt(i)*flag)
{
if (tim[i]!=tot)
c[i]=0,tim[i]=tot;
c[i]++;
}
}
int sum(int i,int flag)//同上,两种查询压一起
{
int ret=0;
for (;i>0&&i<=n;i+=lbt(i)*flag)
if (tim[i]==tot) ret+=c[i];
return ret;
}
void solve(int l,int r)
{
int mid=(l+r)>>1,tp1=l,tp2=mid+1;
if (l==r)
{
printf("%lld\n",ans);
ans-=num[ques[l].pos];
ans+=new_inv[l];//因为如果单纯删除的话,某个逆序对会被重复删除两次
//因此要再加上删除它之后操作序列里新出现的逆序对
return;
}
for (int i=l;i<=r;i++)//按时间划分操作
if (ques[i].x<=mid) newq[tp1++]=ques[i];
else newq[tp2++]=ques[i];
memcpy(ques+l,newq+l,sizeof(Query)*(r-l+1));
solve(l,mid);
tot++;
int j=l;
for (int i=mid+1;i<=r;i++)//更新新出现的逆序对
{
for (;j<=mid&&ques[j].pos<ques[i].pos;j++)
modify(ques[j].num,-1);
new_inv[ques[i].x]+=sum(ques[i].num,1);
}
tot++;
j=mid;
for (int i=r;i>=mid+1;i--)
{
for (;j>=l&&ques[j].pos>ques[i].pos;j--)
modify(ques[j].num,1);
new_inv[ques[i].x]+=sum(ques[i].num,-1);
}
solve(mid+1,r);//剩余部分操作仍然按照之前的关键字排序的顺序分治
tp1=l,tp2=mid+1;
for (int i=l;i<=r;i++)
if ((ques[tp1]<ques[tp2]||tp2>r)&&tp1<=mid) newq[i]=ques[tp1++];
else newq[i]=ques[tp2++];
memcpy(ques+l,newq+l,sizeof(Query)*(r-l+1));
}
int main()
{
freopen("inverse.in","r",stdin);
freopen("inverse.out","w",stdout);
scanf("%d%d",&n,&m);
//树状数组求出初始逆序对
for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=i;
for (int i=1;i<=n;i++)
{
num[i]=sum(a[i],1);
modify(a[i],-1);
ans+=num[i];
}
++tot;
for (int i=n;i>=1;i--)
{
num[i]+=sum(a[i],-1);
modify(a[i],1);
}
for (int i=1;i<=m;i++)
{
scanf("%d",&ques[i].num);
ques[i].pos=b[ques[i].num];
ques[i].x=i;
}
sort(ques+1,ques+m+1);//整个操作序列按pos关键字排序
solve(1,m);
}