【CQOI 2011】动态逆序对

【题目】

传送门

题目描述:

对于序列 a a a ,它的逆序对数定义为满足 i &lt; j i&lt;j i<j,且 a i &gt; a j a_i&gt;a_j ai>aj 的数对 ( i , j ) (i,j) (i,j) 的个数。给 1 1 1 n n n 的一个排列,按照某种顺序依次删除 m m m 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

输入格式:

输入第一行包含两个整数 n n n 和 $m4 ,即初始元素的个数和删除的元素个数。以下 n n n 行每行包含一个 1 1 1 n n n 之间的正整数,即初始排列。以下 m m m 行每行一个正整数,依次为每次删除的元素。

输出格式:

输出包含 m m m 行,依次为删除每个元素之前,逆序对的个数。

样例数据:

输入
5 4
1
5
3
4
2
5
1
4
2

输出
5
2
2
1

备注:

【样例说明】

( 1 , 5 , 3 , 4 , 2 ) → ( 1 , 3 , 4 , 2 ) → ( 3 , 4 , 2 ) → ( 3 , 2 ) → ( 3 ) (1,5,3,4,2)→(1,3,4,2)→(3,4,2)→(3,2)→(3) (1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)

【数据范围】
在这里插入图片描述


【分析】

我们把询问离线下来,就可以倒叙一个一个插入。

那么插入的每一个元素可以用三元组 ( t i m e i , p o s i , v a l i ) (time_i,pos_i,val_i) (timei,posi,vali) 表示。

我们每插入一个数,就相当于是询问 t i m e j &lt; t i m e i time_j&lt;time_i timej<timei,并且 ( p o s i &lt; p o s j &amp; &amp; v a l i &gt; v a l j ) (pos_i&lt;pos_j\&amp;\&amp;val_i&gt;val_j) (posi<posj&&vali>valj),或者 ( p o s i &gt; p o s j &amp; &amp; v a l i &lt; v a l j ) (pos_i&gt;pos_j\&amp;\&amp;val_i&lt;val_j) (posi>posj&&vali<valj) j j j 的个数。

那这就是三维偏序,用 C D Q \mathrm{CDQ} CDQ 分治就可以解决了。


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
int n,m,id[N],bit[N];ll ans[N];
struct node{int x,y,t,num;}a[N],Sort[N];
bool operator<(const node &p,const node &q)
{
	if(p.t!=q.t)  return p.t<q.t;
	if(p.x!=q.x)  return p.x<q.x;
	return p.y<q.y;
}
void add(int i,int x)  {for(;i<=n;i+=lowbit(i))bit[i]+=x;}
int query(int i,int ans=0)  {for(;i;i-=lowbit(i))ans+=bit[i];return ans;}
void solve(int l,int r)
{
	if(l==r)  return;
	int mid=(l+r)>>1,now=l;
	solve(l,mid),solve(mid+1,r);
	int i=l,j=mid+1;
	while(i<=mid||j<=r)
	{
		if(j>r||(i<=mid&&a[i].x<a[j].x))  add(a[i].y,1),Sort[now++]=a[i++];
		else  ans[a[j].t]+=query(n)-query(a[j].y),Sort[now++]=a[j++];
	}
	for(i=l;i<=mid;++i)  add(a[i].y,-1);
	for(i=l;i<=r;++i)  a[i]=Sort[i];
	for(i=r;i>=l;--i)
	{
		if(a[i].t<=mid)  add(a[i].y,1);
		else  ans[a[i].t]+=query(a[i].y);
	}
	for(i=l;i<=r;++i)  if(a[i].t<=mid)  add(a[i].y,-1);
}
int main()
{
	int now,i;
	scanf("%d%d",&n,&m);
	int Time=n;
	for(i=1;i<=n;++i)
	{
		scanf("%d",&now);
		a[i].x=i,a[i].y=now,id[now]=i;
	}
	for(i=1;i<=m;++i)  scanf("%d",&now),a[id[now]].t=Time--;
	for(i=1;i<=n;++i)  if(!a[i].t)  a[i].t=Time--;
	sort(a+1,a+n+1),solve(1,n);
	for(i=1;i<=n;++i)  ans[i]+=ans[i-1];
	for(i=n;i>=n-m+1;--i)  printf("%lld\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值