zoukankan      html  css  js  c++  java
  • [Cqoi2011]动态逆序对

    题意

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

    分析

    参照broxin的题解。

    这本质上是一个三维偏序,分别是时间,下标,数值,记为(t,x,y)。

    我们可以把删除的过程倒过来,当做插入来做,时间t表示这个数是第几个插入的,显然给出的删除的点的t值依次是N,N-1,N-2...(越先删除的视为越后插入的)注意不在询问范围内的点的t值可以任意设置,并且显然没有哪两个点有相同的t或x或y值,这使得问题好考虑得多了。我们求的就是按顺序插入每一个数时,这个数左边比它大的、右边比它小的分别有多少个。形式化地,对一个点(t0,x0,y0),求出满足t<t0,x<x0,y>y0的点的个数记为lda[t0],满足t<t0,x>x0,y>y0的点的个数记为rxiao[t0]。

    我想了一会儿,觉得最外层按x排比较科学,内部对t进行划分排序(相当于快排,将t值<=mid的划分到左边,同时对于划分到同一侧的点要保证原来的相对顺序不变),对y用树状数组来维护。每个节点[L,R]划分出来是这样的:

    要找[L,mid]对[mid+1,R]的贡献:

    先考虑对lda的贡献。枚举t∈[mid+1,R]的点(t0,x0,y0),区间内的点由于已经按时间划分好了,所以不需要考虑t<t0这一条件。只需找出左区间中x<x0且y>y0的点,由于两边的x值各自保持单调(如图),所以可以像树状数组求逆序对一样,将[L,mid]区间内的点的y值在树状数组上增加1,然后求[mid+1,R]的每个y值在树状数组上的前缀和即可。由于l1和l2都是单增的,这一操作复杂度为nlogn。

    再考虑对rxiao的贡献:类似地,找出[L,mid]中x值大于[mid+1,R]中的x值的即可。

    注意一层分治并不能找出[mid+1,R]中所有值的lda和rxiao,但整个分治一定会不遗漏不重复地覆盖每个点的决策区间。每层复杂度nlogn,总共logn层,总复杂度nlog^2n。貌似有人说cdq分治可以做到nlogn?我觉得不太科学,毕竟三维,不可能把某一维直接吃掉吧。。

    cdq分治做这种题真是优秀,空间复杂度仅为O(n),时间复杂度也不逊于高级数据结构,并且分治(对半分)以及树状数组的常数都是极小的,基本是严格logn。

    快排式的cdq,学习了。

    代码

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    #define lowbit(x) (x&-x)
    template<class T>il T read(){
    	rg T data=0,w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    		data=data*10+ch-'0',ch=getchar();
    	return data*w;
    }
    template<class T>il T read(rg T&x){
    	return x=read<T>();
    }
    typedef long long ll;
    
    co int N=1e5+1;
    int n,m;
    namespace T{
    	int c[N];
    	void add(int p,int v){
    		for(int i=p;i<=n;i+=lowbit(i))
    			c[i]+=v;
    	}
    	int sum(int p){
    		int re=0;
    		for(int i=p;i;i-=lowbit(i))
    			re+=c[i];
    		return re;
    	}
    }
    
    struct dot{
    	int t,x,y; // every t,x,y is unique
    }a[N],np[N];
    int lda[N],rxiao[N];
    ll ans[N];
    void cdq(int L,int R){
    	using namespace T;
    	if(L>=R) return;
    	int mid=(L+R)/2;
    	
    	int l1=L,l2=mid+1;
    	for(int i=L;i<=R;++i){
    		if(a[i].t<=mid) np[l1++]=a[i];
    		else np[l2++]=a[i];
    	}
    	std::copy(np+L,np+R+1,a+L);
    	
    	l1=L;
    	for(int i=mid+1;i<=R;++i) // smaller x, bigger y
    	{
    		for(;l1<=mid&&np[l1].x<np[i].x;++l1)
    			add(np[l1].y,1);
    		lda[np[i].t]+=(l1-L)-sum(np[i].y);
    	}
    	for(int i=L;i<l1;++i)
    		add(np[i].y,-1);
    	
    	l1=mid;
    	for(int i=R;i>mid;--i){
    		for(;l1>=L&&np[l1].x>np[i].x;--l1)
    			add(np[l1].y,1);
    		rxiao[np[i].t]+=sum(np[i].y-1);
    	}
    	for(int i=l1+1;i<=mid;++i)
    		add(np[i].y,-1);
    	
    	cdq(L,mid),cdq(mid+1,R);
    }
    
    int pos[N];
    int main(){
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	read(n),read(m);
    	for(int i=1;i<=n;++i)
    		read(a[i].y),a[i].x=i,pos[a[i].y]=i;
    	int t,tmr=n;
    	for(int i=1;i<=m;++i)
    		read(t),a[pos[t]].t=tmr--;
    	for(int i=1;i<=n;++i) if(!a[i].t)
    		a[i].t=tmr--;
    	cdq(1,n);
    	for(int i=1;i<=n;++i)
    		ans[i]=ans[i-1]+rxiao[i]+lda[i];
    	for(int i=n;i>n-m;--i)
    		printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Median Value
    237. Delete Node in a Linked List
    206. Reverse Linked List
    160. Intersection of Two Linked Lists
    83. Remove Duplicates from Sorted List
    21. Merge Two Sorted Lists
    477. Total Hamming Distance
    421. Maximum XOR of Two Numbers in an Array
    397. Integer Replacement
    318. Maximum Product of Word Lengths
  • 原文地址:https://www.cnblogs.com/autoint/p/10372633.html
Copyright © 2011-2022 走看看