【BZOJ 3295】 [Cqoi2011]动态逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB

Submit: 1373  Solved: 465

[Submit][Status][Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下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

CDQ分治+树状数组

orz PoPoQQQ(解题方法和代码都是copy他的。。)

首先我们可以用树状数组求出初始解;

并求出cnt[i],表示i所在的逆序对数:1.在他之前插入了比他大的 2.在他之后插入了比他小的

(注意:为了避免每次都清空树状数组,我们可以用一个时间戳)

但是删除一个数x之后,不能单纯的只减去cnt[x],因为如果逆序对是(x,y),y在之前被删除了,那么之前就已经减过这个逆序对了,这次再减就导致多减了一次。

因此每次在减掉cnt[x]的时候还要加上f[i]:

表示当前要删的数字与之前已经删过的数字构成了几对逆序对。

(这是一个三维的问题了:时间,位置,权值)

我们用CDQ分治来求f[i]。

q[i].x,q[i].y,q[i].pos分别表示删除的数字,删除数字所在的位置,被删除的时间。

按照q[i].y升序排序,递归处理完(l,mid)之后,计算(l,mid)对(mid+1,r)的影响(此时只剩下权值一维):

还是分两种情况:1.在他之前比他大的 2.在他之后比他小的

然后就可以递归解决(mid+1,r)了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define LL long long
#define M 100005
using namespace std;
LL ans=0,f[M],cnt[M];
int t[M],n,m,ti[M],now=0,a[M],b[M];
struct query
{
	int x,y,pos;
}q[M],nq[M];
int lowbit(int x)
{
	return x&(-x);
}
int Getsum(int x,int k)
{
	int ans=0;
	for (int i=x;i&&i<=n;i+=lowbit(i)*k)
		if (ti[i]==now)
			ans+=t[i];
	return ans;
}
void Push_up(int x,int k)
{
	for (int i=x;i&&i<=n;i+=lowbit(i)*k)
	{
		if (ti[i]!=now)
			ti[i]=now,t[i]=0;
		t[i]+=1;
	}
}
bool cmp(query a,query b)
{
	return a.y<b.y;
}
void CDQ(int l,int r)
{
	if (l==r)
	{
		printf("%lld\n",ans);
		ans=ans-cnt[q[l].y]+f[l];
		return;
	}
	int mid=(l+r)>>1;
	int l1=l,l2=mid+1;
	for (int i=l;i<=r;i++)
		if (q[i].pos<=mid)
			nq[l1++]=q[i];
	    else
			nq[l2++]=q[i];
	memcpy(q+l,nq+l,sizeof(q[0])*(r-l+1));
	CDQ(l,mid);
	now++;
	int j=l;
	for (int i=mid+1;i<=r;i++)
	{
		for (;j<=mid&&q[j].y<q[i].y;j++)
			Push_up(q[j].x,-1);
		f[q[i].pos]+=Getsum(q[i].x,1);
	}
	now++;j=mid;
	for (int i=r;i>mid;i--)
	{
		for (;j>=l&&q[j].y>q[i].y;j--)
			Push_up(q[j].x,1);
		f[q[i].pos]+=Getsum(q[i].x,-1);
	}
	CDQ(mid+1,r);
	l1=l,l2=mid+1;
	for (int i=l;i<=r;i++)
		if ((q[l1].y<q[l2].y||l2>r)&&l1<=mid)
			nq[i]=q[l1++];
	    else nq[i]=q[l2++];
	memcpy(q+l,nq+l,sizeof(q[0])*(r-l+1));
}
int main()
{
    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++)
	{
		cnt[i]=Getsum(a[i],1);
		Push_up(a[i],-1);
		ans+=cnt[i];
	}
	now++;
	for (int i=n;i;i--)
	{
		cnt[i]+=Getsum(a[i],-1);
		Push_up(a[i],1);
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d",&q[i].x);
		q[i].y=b[q[i].x];
		q[i].pos=i;
	}
	sort(q+1,q+1+m,cmp);
	CDQ(1,m);
	return 0;
}

时间: 2024-10-06 12:47:40

【BZOJ 3295】 [Cqoi2011]动态逆序对的相关文章

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

[原题] 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 778  Solved: 263 [Submit][Status] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元

【刷题】BZOJ 3295 [Cqoi2011]动态逆序对

Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数 Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数. 以下n行每行包含一个1到n之间的正整数,即初始排列. 以下m行每行一个正整数,依次为每次删除的元素. N<=100000 M<=50000 Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample In

BZOJ 3295: [Cqoi2011]动态逆序对 cdq分治

https://www.lydsy.com/JudgeOnline/problem.php?id=3295 这个妹妹我曾见过的~~~ 之前应该在校内oj写了,似乎还写过题解?发现没写博客就重新水一遍代码水一篇博客好了. 把找逆序对的过程想成一个一个往里塞数字然后找每个数字可以组成的逆序对,把最后要去掉的几个也想成往里塞,所以加一个时间维度用cdq求三维偏序. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorith

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

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

bzoj 3295: [Cqoi2011]动态逆序对

2016-06-22 这个题本想昨天晚上做来,但昨晚狂风大作,暴雨倾盆(听说我们学校最落后的一堵墙都被吹到了),停电了,我只能无聊的瞭望了教学楼一晚上...... 这个题把删除看成插入的话,插入一个点 新增逆序对就是比他早插入的,位置靠前,数比他大或 位置靠后,数比他小.那这就是个三维偏序集,可以用CDQ搞搞了. 这个题也能用树套树,树状数组归并排序做(以后有时间要写一写,恐怕是没可能了%>_<%). 1 #include<cstdio> 2 #include<iostrea

COGS 1715 &amp; bzoj 3295 [CQOI2011]动态逆序对 题解

(又是一道树套树……自己真是玩疯了……) (题意略) 从网上也看过题解,好像解法很多……比如CDQ+树状数组,树状数组套主席树,树状数组套平衡树……我用的是树状数组套splay. (我会说是因为我不会写CDQ和树状数组套主席树么= =) (不得不吐槽,为啥splay这么快= =) 也没啥可说的,我写的是在线算法,只要在删除一个元素之前统计它前面比它大的数和后面比它小的数的个数(区间求和用树状数组,统计比它小/大的数的个数用平衡树写),把答案减掉对应数值即可. 鉴于这题卡常,我就加了快读和各种in

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

[CQOI2011]动态逆序对

P1347 - [CQOI2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数. 以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个

【BZOJ3295】[Cqoi2011]动态逆序对 cdq分治

[BZOJ3295][Cqoi2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的