• LOJ535「LibreOJ Round #6」花火-题解


    题目地址【IN-Loj

    记得开long long,空间要两倍!


    • 题目简述

    给你一个nn的全排列,相邻的之间可以任意交换,只有一次可以交换不相邻的位置的数的机会,求出能让其排好序的最少交换次数。


    分析:

    如果没有那一次不相邻的交换机会,那么就是一个求逆序对个数的裸题了。

    那么有了这个之后,我们暴力的想法是枚举n2n^2的交换情况,每次再nlognnlogn计算逆序对个数,总复杂度为n3lognn^3logn,但是显然过不了。

    所以我们考虑,对于一个点,我们可以看作这样的一个点对(i,hi)(i,h_i),表示下标为ii,高度为hih_i,那么我们交换的点必须要满足:i<j,hi>hji<j,h_i>h_j,否则会增加逆序对的个数。

    而我们将其放在平面上来看,会是这样的:

    eg

    红色的点为(i,hi)(i,h_i),绿色的点为(j,hj)(j,h_j),那么如果交换这两个点,我们可以发现,减少的逆序对个数为1+矩形内的点的个数1+ ext{矩形内的点的个数},因为你原来矩形内的每个点和点jj会产生逆序对,然后矩形内的点和点ii产生逆序对,最后点i,ji,j的逆序对,所以交换后就会减少这么多。

    那么我们只需找到包含点最多的矩形,然后将其左上角的点和右下角的点交换之后答案就为:

    ans=tot矩形内的点的个数×21+1ans=tot- ext{矩形内的点的个数} imes 2 -1+1

    tottot为总的逆序对个数,后面1+1-1+1是因为开始i,ji,j会减少一个,但是你交换i,ji,j又会增加一步操作,所以是这样。


    我们暴力找的话还是n2n^2的,但是我们可以发现,对于一个点(k,hk)(k,h_k),如果k<i,hk>hik<i,h_k>h_i,那么用点k,jk,j肯定比点i,ji,j要优秀,因为这两个矩形是包含的关系,如下图:

    eg

    图上点CC为点kkAA为点iiBB为点jj

    同理,对于一个点(w,hw)(w,h_w),如果w>j,hw<hjw>j,h_w<h_j,那么i,wi,w肯定比i,ji,j要优秀,如下图:

    eg

    AA为点ii,点CC为点jj,点EE为点ww


    所以根据这个单调性,我们可以用整体二分+主席树找到包含点最多的矩形,具体细节参考下面这个博客【Orz-IN

    但是,这个方法是O(nlog2n)O(nlog^2n)的,所以虽然能过,但是常数巨大且不好写。


    不妨反过来思考。
    我们可以对于每一个点,找出包含它的最大的矩形。
    那么我们对于点(i,hi)(i,h_i),我们前面找最小的ll,且满足l<i,hl<hil<i,h_l<h_i,找后面最大的rr,且满足r>i,hr<hir>i,h_r<h_i,这个就是能包含它的最大的矩形了,所以对于所有点(j,hj)(j,h_j),只要满足ljr,hrhjhllleq jleq r,h_rleq h_jleq h_l,那么这个点就会对其作出贡献。

    我们对于矩形不好处理,所以将其转化为点对,一个点(x,y)(x,y)表示左上角的下标在xx右下角的下标在yy的矩形,那么我们每次只用在(li1,i+1r)(lsim i-1,i+1sim r)内的点加11即可。

    所以我们用扫描线的思想将一个矩形拆成两条线,一条入线,一条出线。
    我们按照新的平面的yy坐标从下往上扫,所以我们将原来一个(i,hi)(i,h_i)点表示的矩形拆为(li1,i+1)(lsim i-1,i+1)的一条线和(li1,r+1)(lsim i-1,r+1)的两条线,当我们遇到第一条线时表示进入了这个矩形,就将li1lsim i-111,遇到第二条线就表示已经出了这个矩形,我们要将这个矩形的影响消除,就将li1lsim i-111,然后我们每次取当前这条线上的最大值更新最多点矩阵的点数即可,这个可以用一个线段树维护。

    所以先预处理,用归并排序或者树状数组的方式求出总的逆序对的个数tottot,记最多的点数为pp,答案就为:
    tot2×ptot-2 imes p

    所以在nlognnlogn的预处理,nlognnlogn的求ppO(1)O(1)回答答案即可。
    总复杂度O(nlogn)O(nlogn),比分治的O(nlog2n)O(nlog^2n)要优秀的多。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int M=3e5+10;
    int n,H[M];
    int lmax[M],rmin[M];
    struct node{
    	int l,r,type,ck;
    	node(){}
    	node(int a,int b,int c,int d):l(a),r(b),type(c),ck(d){}
    	bool operator <(const node &a)const{return ck<a.ck||(ck==a.ck&&type>a.type);}
    }line[M<<1];//因为每个矩形拆成两条线,所以空间开两倍
    int tot;
    int maxv[M<<2],lazy[M<<2];
    void pushup(int o){
    	maxv[o]=max(maxv[o<<1],maxv[o<<1|1]);
    }
    void pushdown(int o){
    	if(!lazy[o]) return;
    	maxv[o<<1]+=lazy[o];
    	maxv[o<<1|1]+=lazy[o];
    	lazy[o<<1]+=lazy[o];
    	lazy[o<<1|1]+=lazy[o];
    	lazy[o]=0;
    }
    void update(int o,int l,int r,int L,int R,int v){
    	if(L<=l&&r<=R){
    		maxv[o]+=v;lazy[o]+=v;
    		return;
    	}
    	int mid=l+r>>1;
    	pushdown(o);
    	if(L<=mid) update(o<<1,l,mid,L,R,v);
    	if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
    	pushup(o);
    }
    //线段树区间加维护最大值
    #define lowbit(a) ((a)&(-(a)))
    ll bit[M];
    void add(int a,ll b){
    	for(;a<=n;a+=lowbit(a))bit[a]+=b;
    }
    ll query(int a){
     	int ans=0;
    	for(;a;a-=lowbit(a))ans+=bit[a];
    	return ans;	
    }
    //树状数组求逆序对个数
    ll ans;//记得开long long
    void init(){
    	for(int i=n;i>=1;i--){
    		ans+=1ll*query(H[i]);
    		add(H[i],1);
    	}	rmin[n+1]=n+1;
    	for(int i=1;i<=n;i++)lmax[i]=max(lmax[i-1],H[i]);
    	for(int i=n;i>=1;i--)rmin[i]=min(rmin[i+1],H[i]);
    	//求取前缀最大和后缀最小
    	int l,r;
    	for(int i=1;i<=n;i++){
    	//查询l,r,并加入扫描线
    		l=lower_bound(lmax+1,lmax+n+1,H[i])-lmax;
    		r=lower_bound(rmin+1,rmin+n+1,H[i])-rmin;
    		if(rmin[r]>H[i])--r;
    		if(l>=i||r<=i)continue;//排除不合法的情况
    		line[++tot]=node(l,i-1,1,i+1);
    		line[++tot]=node(l,i-1,-1,r+1);
    	}
    	sort(line+1,line+tot+1);//按照y排序
    }
    ll maxdel;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&H[i]);
    	init();
    	for(int i=1;i<=tot;i++){
    		update(1,1,n,line[i].l,line[i].r,line[i].type);
    		if(maxv[1]>maxdel)maxdel=maxv[1];//更新找最大
    	}
    	printf("%lld
    ",ans-2ll*maxdel);//输出答案
    	return 0; 
    } 
    

    类似题目【IN

  • 相关阅读:
    python面向对象--元类
    python面向对象--类的装饰器
    python--异常处理
    python面向对象--类的内置函数
    python面向对象--类的内置方法
    关于discuz论坛邮箱配置
    python面向对象--item方法
    python面向对象--包装标准类型及组合方式授权
    python面向对象--反射机制
    python面向对象的三大特征--封装
  • 原文地址:https://www.cnblogs.com/VictoryCzt/p/10053401.html
Copyright © 2020-2023  润新知