Codeforces Round #751 (Div. 2) E. Optimal Insertion

发布于 2021-11-10  538 次阅读


这是一道好题,先放一下题目的链接吧:https://codeforces.com/contest/1602/problem/E

b数组要插入到a数组当中,先说明一个事情,b既然是无序插入,那我们可以让b在合成的新数组当中呈升序,这样会尽可能地减少逆序对的个数。下面来证明一下这个观点

假设数字bi选择了它的最佳插入位置,我们将其称为pi(意思为插在a数组下标为 pi的后面 ),此时出现了一个新的数字bjbj> bi ,我们先让pj= pi ,如此时 pj 减小一定的数字,并不会使以 pj 为分割的数列后半部分的对逆序对总数的贡献减少,而对于前半部分 bi 是最佳答案, bj 减小只有可能会增大最后对逆序对的贡献。证毕

得知上述结论,肯定可以选择先去排序了。bmid找到最佳位置后任何bi(i>mid),都不可能插在 bmid 所插在数字的左边,反之亦然。递归分治!!!最后得出合成后的数组,求逆序对即可

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[1000500],b[1000500],c[2000500],tmp[2000500];
vector<int>v[1000500];
ll ans=0;
void qsort(int l,int r){
	//cout<<l<<" "<<r<<endl;
	if(l>=r) return;
	//cout<<l<<" "<<r<<endl;
	int mid=(l+r)>>1;
	qsort(l,mid);
	qsort(mid+1,r);
	
	int i=l,j=mid+1,cnt=i-1;
	while(i<=mid&&j<=r){
		if(c[i]<=c[j]){
			tmp[++cnt]=c[i];
			i++;
		}
		else{
			tmp[++cnt]=c[j];
			ans+=(mid-i+1);
			j++;
		}
	}
	while(i<=mid) tmp[++cnt]=c[i],i++;
	while(j<=r) tmp[++cnt]=c[j],j++;
	for(i=l;i<=r;i++) c[i]=tmp[i];
}
void solve(int l,int r,int L,int R){
	if(l>r||L>R) return;
	int mid=(l+r)>>1;
	int cnt=0,index=L,sum=0;
	for(int i=L+1;i<=R;i++){
		if(a[i]<b[mid]) cnt--;
		if(a[i]>b[mid]) cnt++;
		if(sum>cnt){
			index=i;
			sum=cnt;
		}
	}
	v[index].push_back(b[mid]);
	solve(l,mid-1,L,index);
	solve(mid+1,r,index,R);
}
int main(){
	int T;
	cin>>T;
	while(T--){
		ans=0;
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=0;i<=n;i++) v[i].clear();
		for(int i=1;i<=m;i++) scanf("%d",&b[i]);
		sort(b+1,b+1+m);
		solve(1,m,0,n);
		int cnt=0;
		sort(v[0].begin(),v[0].end());
		for(int j:v[0]) c[++cnt]=j;
		//if(cnt==1) cout<<c[1]<<endl;
		//cout<<a[1]<<endl;
		int sum=0;
		for(int i=1;i<=n;i++){
			sort(v[i].begin(),v[i].end());
			sum+=v[i].size();
			c[++cnt]=a[i];
			for(int j:v[i]) c[++cnt]=j;
		}
		//cout<<sum+v[0].size()<<endl;
		//for(int i=1;i<=cnt;i++) cout<<c[i]<<" ";
		//cout<<endl;
		qsort(1,cnt);
		cout<<ans<<endl;
	}
	return 0;
}

回忆这理想不够理想,沿途逛世间一趟只有向上