【cdq分治】[HYSBZ/BZOJ3295]动态逆序对
题目
看看这篇博客写的时间,BZOJ已经挂了,我就不粘BZOJ链接了。
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
Hint
样例解释
(1,5,3,4,2)>>(1,3,4,2)>>(3,4,2)>>(3,2)>>(3)。
数据范围
编号 1-2 3-4 5-6 7-8 9-10
n <=1000 <=30000 <=50000 <=60000 <=100000
m <=100 <=10000 <=20000 <=40000 <=50000
分析
这道题
我们令y表示当前数字,t表示y被加入(先被删除的后加入)的时间,x表现y在原串中的位置,这样一个数字就变成了一个三元组(t,x,y)。
不难发现,当一个数字(t0,x0,y0)对最终的逆序对数作出的贡献为存在的三元组(t,x,y)使得
这样,我们就可以用cdq分治做这道题。
如果你已经会cdq请直接看代码。
如何用cdq分治做这道题
This part was powered by azui
即
我们先考虑满足条件一:
在外面按x排序后,还剩下t,y两个参数。我们可以对t进行划分排序,使得t,x满足。
然后就可以求出对于每一个右边的三元组(t0,x0,y0),有多少个点满足
条件二类似。
然后递归处理左右两边。
感谢azui大神,你们也可以去看他自己的博客。
代码
#include<cstdio>
#include<algorithm>
#define MAXN 100000
using namespace std;
struct node{
int t,x,y;
node(){
}
node(int tt,int xx,int yy){
t=tt,x=xx,y=yy;
}
}p[MAXN+10],tmp[MAXN+10];
int n,m,a[MAXN+10],t[MAXN+10],c[MAXN+10];
long long ans[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
inline int lowbit(int x){
return x&-x;
}
void update(int x,int d){
while(x<=n){
c[x]+=d;
x+=lowbit(x);
}
}
int get_sum(int x){
int ret=0;
while(x){
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void read(){
Read(n),Read(m);
int i,b,j=0;
for(i=1;i<=n;i++)
Read(a[i]);
for(i=1;i<=m;i++){
Read(b);
t[b]=i;
}
for(i=1;i<=n;i++)
if(t[a[i]])
p[i]=node(n-t[a[i]]+1,i,a[i]);
else
p[i]=node(++j,i,a[i]);
}
void cdq(int l,int r){
if(l==r)
return;
int mid=(l+r)>>1,i,j,k;
k=mid+1,j=l;
for(i=l;i<=r;i++)
if(p[i].t<=mid)
tmp[j++]=p[i];
else
tmp[k++]=p[i];
for(i=l;i<=r;i++)
p[i]=tmp[i];
i=l;
for(j=mid+1;j<=r;j++){
for(;i<=mid&&p[i].x<p[j].x;i++)
update(p[i].y,1);
ans[p[j].t]+=(i-l)-get_sum(p[j].y);
}
for(i--;i>=l;i--)
update(p[i].y,-1);
i=mid;
for(j=r;j>mid;j--){
for(;i>=l&&p[i].x>p[j].x;i--)
update(p[i].y,1);
ans[p[j].t]+=get_sum(p[j].y);
}
for(i++;i<=mid;i++)
update(p[i].y,-1);
cdq(l,mid);
cdq(mid+1,r);
}
void print(){
int i;
for(i=2;i<=n;i++)
ans[i]+=ans[i-1];
for(i=n;i>n-m;i--)
printf("%I64d\n",ans[i]);
}
int main()
{
read();
cdq(1,n);
print();
}