Luogu1393动态逆序对(CDQ分治)

Posted 小蒟蒻yyb的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu1393动态逆序对(CDQ分治)相关的知识,希望对你有一定的参考价值。

【Luogu1393】动态逆序对(CDQ分治)

题面

题目描述

对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数。你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数。

输入输出格式

输入格式:

第一行,两个数n,m,表示序列中有n个数,要删去m个数

第二行n个数,表示给定的序列。

第三行m个数,第i个数di表示要删去原序列中的第di个数。

输出格式:

一行m+1个数。第一个数表示给定序列的逆序对组数,第i+1个数表示删去第di个数后序列的逆序对组数(删去的数不再恢复)

输入输出样例

输入样例#1:

6 3
5 4 2 6 3 1
2 1 4

输出样例#1:

11 7 4 2

说明

对于20%的数据,n≤2500

对于另30%的数据,m=0

对于100%的数据,n≤40000,m≤n/2,且保证第二行n个数互不相同,第三行m个数互不相同

题解

之前不是说过要写一遍CDQ分治吗??
在这里说的
可是,当你把上面的代码兴高采烈的Copy到洛谷上之后
你就会直接WA了
因为,题目还是有点不同的(仔细读题)
区别一:这题不是排列,要离散化
区别二:这题删掉的不是数字,而是位置

好了回归正题,讲讲CDQ分治怎么写
首先,给所有删掉的数编个号,就按照删去的顺序来吧
没有删掉的数就编个INF吧

那么,删掉这个数之后,减少的逆序对对数是:
对于\\(j\\in[1,j]\\)
\\(t[i]<t[j]\\),其中t是删除的编号
并且
\\(i<j,a[i]>a[j]\\)
或者
\\(i>j,a[i]<a[j]\\)
所以,删除的编号直接sort搞完
剩下的两维CDQ分治

于是,发现这个玩意是一个三维偏序
所以之前写过的树状数组套平衡树当然也可以做啦
但是,CDQ分治还是要会嗷。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 50000
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while((ch<\'0\'||ch>\'9\')&&ch!=\'-\')ch=getchar();
	if(ch==\'-\')t=-1,ch=getchar();
	while(ch<=\'9\'&&ch>=\'0\')x=x*10+ch-48,ch=getchar();
	return x*t;
}
int n,m,S[MAX],a[MAX],b[MAX],c[MAX],d[MAX];
long long ans;
int lowbit(int x){return x&(-x);}
void Add(int x,int w){while(x<=n)c[x]+=w,x+=lowbit(x);}
int getsum(int x){int ret=0;while(x)ret+=c[x],x-=lowbit(x);return ret;}
struct Node
{
	int t,p,a;
	int s;
}t[MAX];
bool operator<(Node a,Node b){return a.t<b.t;}
bool cmp(Node a,Node b){return a.p<b.p;}
void CDQ(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	CDQ(l,mid);CDQ(mid+1,r);
	sort(&t[l],&t[mid+1],cmp);
	sort(&t[mid+1],&t[r+1],cmp);
	int j=mid;
	for(int i=l;i<=mid;++i)
	{
		while(j<r&&t[j+1].p<t[i].p)++j,Add(t[j].a,1);
		t[i].s+=getsum(n)-getsum(t[i].a);
	}
	for(int i=mid+1;i<=j;++i)Add(t[i].a,-1);
	j=r+1;
	for(int i=mid;i>=l;--i)
	{
		while(j>mid+1&&t[j-1].p>t[i].p)--j,Add(t[j].a,1);
		t[i].s+=getsum(t[i].a-1);
	}
	for(int i=r;i>=j;--i)Add(t[i].a,-1);
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)S[i]=a[i]=read();
	sort(&S[1],&S[n+1]);
	for(int i=1;i<=n;++i)b[a[i]=lower_bound(&S[1],&S[n+1],a[i])-S]=i;
	for(int i=n;i;i--)ans+=getsum(a[i]),Add(a[i],1);
	for(int i=1;i<=n;++i)t[i].t=n+1,t[i].p=i,t[i].a=a[i];
	for(int i=1;i<=m;++i)
	{
		d[i]=read();
		t[d[i]].t=i;
	}
	sort(&t[1],&t[n+1]);
	memset(c,0,sizeof(c));
	CDQ(1,n);
	for(int i=1;i<=n;++i)c[t[i].p]=t[i].s;
	printf("%lld ",ans);
	for(int i=1;i<=m;++i)
		printf("%lld ",ans=ans-c[d[i]]);
	return 0;
}

以上是关于Luogu1393动态逆序对(CDQ分治)的主要内容,如果未能解决你的问题,请参考以下文章

LUOGU P3157 [CQOI2011]动态逆序对(CDQ 分治)

luogu P3157 [CQOI2011]动态逆序对(CDQ分治)

P3157 动态逆序对 CDQ分治

洛谷 P3157 [CQOI2011]动态逆序对 | CDQ分治

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

BZOJ 3295 动态逆序对 | CDQ分治