【DS】loj#535. 「LibreOJ Round #6」花火

题传

真难写,,

考虑冒泡排序,每一次交换相邻的减少 1 对逆序对,那么我们肯定要第二种操作尽量多地减少逆序对。

考虑第二种操作 swap(i,j) 减少的逆序对数量:\(\forall k\in(i,j),h_j<h_k<h_i\),\(k\) 的个数乘二。

考虑抽象到二维平面,点坐标为 \((i,h_i)\),那么就是以 \((i,h_i)\) 为左上角,\((j,h_j)\) 为右下角的矩形内部点数量。

但这样暴力算还是 \(O(n^2\log n)\)。

看看选择的点有什么性质。

自己画画图,既然 \(i\) 为左上角,那么 \(i\) 应该是单增的,所以应该选择的是前缀最大值的点。

这时候可能有小可爱跟我一样 SB,想了好久 \(j\) 是前缀最小值还是后缀最小值。

【DS】loj#535. 「LibreOJ Round #6」花火

黑色的是前缀最大值的点,红色是前缀最小值,蓝色是后缀最小值。

然后我思考了很久发现不知道选择是前缀还是后缀。

事实上图画错了!

所以显然是右端点是后缀最小值的点。

这样暴力维护也没用,找找性质,枚举右端点?可惜左端点并不单增。

考虑正难则反,让 \(k\) 去贡献每对 \((i,j)\),即每个矩形。

需要满足的是 \(i<k,h_i>h_k,j>k,h_j<h_k\),发现因为连续区间且维护的前缀最大、后缀最小都单调,因此二分找出合法区间,变成二维平面矩形加、点最大值。扫描线即可。

//swap(i,j)-> -= i<k<j ,a[i]>a[k],a[j]<a[k],a[j]<a[k]<a[i]
//二维平面(i,a[i]),矩形查

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
#define N (int)(3e5+5)
int n,a[N],sum[N];

int lowbit(int x) {
	return x&(-x);
}
void add(int x,int v) {
	while(x<=n) sum[x]+=v,x+=lowbit(x);
}
int qry(int x) {
	int res=0; while(x) res+=sum[x],x-=lowbit(x); return res;
}

namespace zzx {
	int Ls[N*32],Rs[N*32],sum[N*32],rt[N],tot=0;
	void update(int pre,int &cur,int l,int r,int pos) {
		if(!cur) cur=++tot;
		sum[cur]=sum[pre]+1;
		if(l==r) return ;
		int mid=(l+r)>>1;
		if(pos<=mid) Rs[cur]=Rs[pre],update(Ls[pre],Ls[cur],l,mid,pos);
		else Ls[cur]=Ls[pre],update(Rs[pre],Rs[cur],mid+1,r,pos);
	}
	int query(int pre,int cur,int l,int r,int cl,int cr) {
		if(cl>cr) return 0;
		int qwq=sum[cur]-sum[pre];
		if(cl<=l&&r<=cr) return qwq;
		int mid=(l+r)>>1,res=0;
		if(cl<=mid) res=query(Ls[pre],Ls[cur],l,mid,cl,cr);
		if(cr>mid) res+=query(Rs[pre],Rs[cur],mid+1,r,cl,cr);
		return res;
	}
}
using namespace zzx;
vector<int>L,R;
struct node {
	int l,r,v;
	node() {
		
	}
	node(int nl,int nr,int nv) {
		l=nl; r=nr; v=nv;
	}
};
vector<node>p[N];

namespace XDS {
	#define ls (cur<<1)
	#define rs (ls|1)
	int mx[N<<2],tag[N<<2];
	void push_up(int cur) {
		mx[cur]=max(mx[ls],mx[rs]);
	}
	void push_down(int cur) {
		if(tag[cur]==0) return ;
		mx[ls]+=tag[cur]; mx[rs]+=tag[cur];
		tag[ls]+=tag[cur]; tag[rs]+=tag[cur];
		tag[cur]=0;
	}
	void update(int cur,int l,int r,int cl,int cr,int x) {
		if(cl<=l&&r<=cr) {
			mx[cur]+=x; tag[cur]+=x; return ;
		}
		push_down(cur);
		int mid=(l+r)>>1;
		if(cl<=mid) update(ls,l,mid,cl,cr,x);
		if(cr>mid) update(rs,mid+1,r,cl,cr,x);
		push_up(cur);
	}
	int query(int cur,int l,int r,int cl,int cr) {
		if(cl<=l&&r<=cr) {
		//	cout<<mx[cur]<<'\n';
			return mx[cur]; 
		}
		push_down(cur);
		int mid=(l+r)>>1,res=0;
		if(cl<=mid) res=query(ls,l,mid,cl,cr);
		if(cr>mid) res=max(res,query(rs,mid+1,r,cl,cr));
		return res;
	}
}
using namespace XDS;
bool vis[N];
signed main() {
	//freopen("columns9_3.in","r",stdin);
	cin.tie(0);
	std::ios::sync_with_stdio(false);
	cin>>n; int ans=0;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) {
		ans+=qry(n)-qry(a[i]);	
		add(a[i],1);
	}	
	for(int i=1;i<=n;i++) update(rt[i-1],rt[i],1,n,a[i]);
//	cout<<ans<<endl;
	int mx=0; a[0]=0;
	for(int i=1;i<=n;i++) {
		//cout<<a[i]<<" "<<a[mx]<<endl;
		if(a[i]>a[mx]) {
			mx=i; L.pb(i); vis[i]=1;//cout<<"?";
		}
	}
	a[0]=(int)(2e18); int mi=0;
	for(int i=n;i>=1;i--) {
		if(a[i]<a[mi]) {
			mi=i; R.pb(i); vis[i]=1;
		}
	}
	reverse(R.begin(),R.end());
	//cout<<L.size()<<" "<<R.size()<<'\n';
	for(int x=1;x<=n;x++) {
		if(vis[x]) continue;
		int l=0,r=L.size()-1,res=-1,xl=-1,xr=-1,yl=-1,yr=-1;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(a[L[mid]]>a[x]) res=mid,r=mid-1;
			else l=mid+1;
		}
	//	if(res!=-1) cout<<res<<endl;
		if(res!=-1) {
			xl=L[res]; xr=x-1;
		}
		l=0; r=R.size()-1; res=-1;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(a[R[mid]]<a[x]) res=mid,l=mid+1;
			else r=mid-1;
		}
		if(res!=-1) {
			yl=x+1; yr=R[res];
		}
		if(xl!=-1&&yl!=-1) {
			if(xl>xr||yl>yr) continue;
		//	cout<<xl<<" "<<xr<<" "<<yl<<" "<<yr<<'\n';
			p[xl].pb(node(yl,yr,1)); p[xr+1].pb(node(yl,yr,-1));
		}
	}
	int res=0;
	for(int i=1;i<=n;i++) {
		for(node x:p[i]) {
		//	cout<<x.l<<" "<<x.r<<endl;
			update(1,1,n,x.l,x.r,x.v);
		}
		res=max(res,query(1,1,n,1,n));
	}
	cout<<ans-2ll*res<<'\n';
	return 0;
}
上一篇:Qt之开发环境配置——在VS2008中为QT增加代码提示功能


下一篇:如何实现卖家增长任务的AB实验