zoukankan      html  css  js  c++  java
  • 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
    ",d[i]);
    }
    
    我是咸鱼。转载博客请征得博主同意Orz
  • 相关阅读:
    vue项目,百度地图api高亮选取区域,高亮某个地区,行政区域等
    vue 项目, 通知子组件更新,父组件中每次点击按钮重新加载子组件,(重新生成dom 元素)
    洛谷 P1003 铺地毯
    Codeforces Round #582 (Div. 3)
    安科 OJ 1190 连接电脑 (并查集)
    2018年牛客多校寒假 第四场 F (call to your teacher) (图的连通性)
    牛客小白月赛16 A 小石的签到题 ( 博弈)
    牛客小白月赛16 E 小雨的矩阵 ( 暴搜)
    安科 OJ 1054 排队买票 (递归,排列组合)
    牛客小白月赛15 C 表单 ( map 使用)
  • 原文地址:https://www.cnblogs.com/sdfzhsz/p/9281364.html
Copyright © 2011-2022 走看看