nlogn求逆序对&&陌上花开

前置:
nlogn逆序对:
前一个小时我还真的不会这个Orz
这里运用归并排序的思想。
对于一个序列,我们把它先分开,再合并成一个有序序列。
引自https://blog.csdn.net/qq_30189255/article/details/50937307

假设f(i,j)为i到j号元素中的逆序对个数,取一个分割点k,假设s(i,j,k)表示以k为分割点,第一个元素在i到k中,第二个元素在k+1到j中形成的逆序对数。那么我们就得到一个递归式:f(i,j)=f(i,k)+f(k+1,j)+s(i,j,k)。很自然的与归并排序联系到了一起,对于更小规模的f可以递归求解,如果对于a数组的i到k以及k+1到j两个部分元素均为有序的情况,那么对于当a[j]<a[i]的情况,必然有a[j]<a[i..k],即a[j]和i到k号元素都形成逆序对,此时只要将s(i,j,k)加上k-i+1就可以了(i到k之间的元素个数),这个过程很像归并排序的Merge的过程——比较当前i和j的状态并放入较小的。于是我们就得到了算法,和归并排序一起操作:外围设置一个计数器count,每次归并过程分为分割与合并两个部分,分割照常处理,在合并部分中的if (a[i]>a[j]) b[++l]=a[++j];中加入一个计数过程,即cnt+=k-i+1。这样当排序完成后,统计也就完成了。

代码如下

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[50005],b[50005];
int merge(int l,int r) {
	int ans=0;
	if(l>=r) return 0;
	int mid=l+r>>1;
	ans+=merge(l,mid),ans+=merge(mid+1,r);
	int i=l,j=mid+1,k=0,cnt=0;
	while(i<=mid&&j<=r) {
		if(a[i]<=a[j]) b[k++]=a[i++];
		else cnt+=mid-i+1,b[k++]=a[j++];
	} 
	while(j<=r) b[k++]=a[j++];
	while(i<=mid) b[k++]=a[i++];
	for(k=0;k<r-l+1;k++) {
		a[l+k]=b[k];
	}
	return cnt+ans;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	cout<<merge(1,n);
}

这种问题又叫二维偏序,因为有两个维度:位置,数值

那么三维偏序呢?

IOI2019金牌选手GhostCai发明的czq分治
dalao陈丹琦发明的算法叫cdq分治
例题:陌上花开https://www.luogu.org/problemnew/show/P3810
cdq+树状数组 引自 https://blog.csdn.net/reverie_mjp/article/details/52462651

【对于本题来说,每个操作包含三维,首先按第一维关键字排序,并去重,数组中记录相同的花有多少朵。然后CDQ分治处理,处理时,将[l,mid]区间和[mid+1,r]区间分别按第二维关键字排序,并用树状数组以第三维为下标,维护每一朵花的出现次数。每一次处理[l,mid]对[mid+1,r]的影响时,只需考虑第二维的影响即可(因为[l,mid]区间的x一定小于[mid+1,r]区间的x,而第三维用树状数组维护也不需要考虑),当第二维符合要求时,将它的影响加入树状数组中。每查找完[mid+1,r]区间的一个操作,就更新答案】

#include <cstdio>
#include <algorithm>
using namespace std;
int t[500005];
int n,m;
const int N=500005;
struct Node {
	int x,y,z,cnt,ans;
	bool operator == (const Node &rhs)const {
		return x==rhs.x&&y==rhs.y&&z==rhs.z;
	}
} a[N],stk[N];
bool cmp(Node x,Node y) {
	return x.x==y.x?(x.y==y.y?x.z<y.z:x.y<y.y):x.x<y.x;
}
void add(int x,int y) {
	for(int i=x; i<=m; i+=i&-i) 
		t[i]+=y;
}
int ask(int x) {
	int res=0;
	for(int i=x; i; i-=i&-i) 
		res+=t[i];
	return res;
}
void merge(int l,int r) {
	if (l==r) 
		return;
	int mid=l+r>>1;
	merge(l,mid);
	merge(mid+1,r);
	int i=l,j=mid+1,k=0;
	while(i<=mid&&j<=r) {
		while(i<=mid&&a[i].y<=a[j].y) add(a[i].z,a[i].cnt),stk[++k]=a[i++];
		while(j<=r&&a[j].y<a[i].y) a[j].ans+=ask(a[j].z),stk[++k]=a[j++];
	}
	while(i<=mid) add(a[i].z,a[i].cnt),stk[++k]=a[i++];
	while(j<=r)  a[j].ans+=ask(a[j].z),stk[++k]=a[j++];
	for(int i=l; i<=mid; i++) add(a[i].z,-a[i].cnt);
	for(int i=l; i<=r; i++) a[i]=stk[i-l+1];

}
int d[N];
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) {
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].cnt=1;
	}
	sort(a+1,a+1+n,cmp);
	int cnt=0;
	for(int i=1; i<=n; i++) {
		if(a[i]==a[i-1]&&i!=1) a[cnt].cnt++;
		else cnt++,a[cnt]=a[i];
	}
	swap(n,cnt);
	merge(1,n);
	for(int i=1; i<=n; i++) d[a[i].ans+a[i].cnt]+=a[i].cnt;
	for(int i=1; i<=cnt; i++) printf("%d\n",d[i]);
}
posted @ 2018-07-08 21:28  SWHsz  阅读(361)  评论(0编辑  收藏  举报