CF1602E题解

题面:Problem - 1602E - Codeforces

结论1

b一定升序排列。

证明:

image
如果存在两个b(黑点)为降序放置,考虑此时一个数a(红点)的影响:
image

后面只要有比a小的,逆序对就增加多少。a在后面时同理。

考虑a在中间:
image

当a的值为蓝色点时,交不交换都会有一个逆序对,而当a为红点时,交换会使逆序对数减 \(2\)

于是,b升序一定不劣。

证毕。

结论2(十分通用)

命名:我们把满足放一个b到a产生的新逆序对数量最少的位置叫做最佳位置

对于一组 \((b_{1},b_{2})\),若满足 \(b_{1}<b_{2}\) ,其最佳位置 \((p_{1},p_{2})\) ,必定满足 \(p_{1}<p_{2}\)

证明:

反证法。设 \(p_{2}<p_{1}\)

我们记 \(c[i][j]\) 表示把 \(b_{i}\) 放到 \(p_{j}\) 位置时区间 \([p_{2},p_{1}]\) 产生的新逆序对个数。

\(p1,p2\) 的定义,我们知道 $c[1][1]\le c[1][2] $ ,\(c[2][2]\le c[2][1]\)

考虑一个数插入到一个数列的个数是其左边比它大的数和其右边比它小的数,令比它小的数为 \(0\) ,比它大的数为 \(1\) ,既是左边 \(1\) 的个数加上右边 \(0\) 的个数。

若把 \(b_{2}\) 放到 \(p_{1}\) 上,因为 \(b_{2}>b_{1}\) ,左边的 \(1\) 变为 \(0\) ,右边的变化不在区间内。

则有:\(c[1][1] \ge c[2][1]\)\(c[2][2]\ge c[2][1]\)

\(c[2][2] \ne c[2][1]\) 时,矛盾。

\(c[2][2]=c[2][1]\) 时,可以直接交换 \(p_{1},p_{2}\)

证毕

于是本题的做法就很简单了,后续做法可以查看这位大佬的博客

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6,INF=(1<<30);
int T,n,m,b[N+5],tmp[N+5];ll ans;
struct node {int v,p;} a[N+5];
struct BIT
{
	int c[N+5];
	void add(int x,int v) {for(int i=x; i<=n; i+=i&(-i)) c[i]+=v;}
	int ask(int x) {int ret=0;if(!x) return 0;for(int i=x; i>=1; i-=i&(-i)) ret+=c[i];return ret;}
} B;
int mn[4*N+5],tag[4*N+5],pos[4*N+5];
int cmp(node n1,node n2) {return n1.v<n2.v;}
void build(int k,int l,int r)
{
	tag[k]=0;if(l==r) return mn[k]=l-1,void();
	int mid=(l+r)>>1;build(k*2,l,mid);build(k*2+1,mid+1,r);
	mn[k]=min(mn[k*2],mn[k*2+1]);
}
void pushdown(int k) {mn[k*2]+=tag[k];mn[k*2+1]+=tag[k];tag[k*2]+=tag[k];tag[k*2+1]+=tag[k];tag[k]=0;}
void update(int k,int l,int r,int x,int y,int v)
{
	if(x>r||y<l) return;
	if(x<=l&&r<=y) return mn[k]+=v,tag[k]+=v,void();
	pushdown(k);int mid=(l+r)>>1;
	update(k*2,l,mid,x,y,v);update(k*2+1,mid+1,r,x,y,v);
	mn[k]=min(mn[k*2],mn[k*2+1]);
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);ans=0;
		for(int i=1; i<=n; i++) scanf("%d",&a[i].v),a[i].p=i;
		for(int i=1; i<=m; i++) scanf("%d",&b[i]);
		sort(a+1,a+1+n,cmp);sort(b+1,b+1+m);build(1,1,n+1);
		int p1=1,p2=1;
		for(int i=1; i<=m; i++)
		{
			while(p1<=n&&a[p1].v<b[i]) update(1,1,n+1,1,a[p1].p,1),++p1;
			while(p2<=n&&a[p2].v<=b[i]) update(1,1,n+1,a[p2].p+1,n+1,-1),++p2;ans+=mn[1];
		}
		int tot=0;for(int i=1; i<=n; i++) tot+=(i==1||a[i].v!=a[i-1].v),tmp[a[i].p]=tot;
		for(int i=n; i>=1; i--) ans+=B.ask(tmp[i]-1),B.add(tmp[i],1);
		for(int i=n; i>=1; i--) B.add(tmp[i],-1);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-10-27 20:19  keepcoder  阅读(56)  评论(1编辑  收藏  举报