P1966 火柴排队
显然有一个想法,把 $A,B$ 从小到大一一对应,这样距离最小,证明的话好像挺显然的
设 $A_i<A_{i+1}$ ,$B_{i}<B_{i+1}$,那么如果 $A_i$ 对应 $B_i$,$A_{i+1}$ 对应 $B_{i+1}$
距离为 $(A_i-B_i)^2+(A_{i+1}-B_{i+1})^2=A_{i}^{2}-2A_iB_i+B_{i}^2+A_{i+1}^{2}-2A_{i+1}B_{i+1}+B_{i+1}^2$
如果 $A_i$ 对应 $B_{i+1}$ ,$A_{i+1}$ 对应 $B_{i}$ 那么距离同样可以算出,为
$A_{i}^2-2A_{i}B_{i+1}+B_{i+1}^2+A_{i+1}^2-2A_{i+1}B{i}+B_{i+1}^2$
两式相减,得到 $-2(A_iB_i+A_{i+1}B_{i+1}-A_{i+1}B_{i}-A_{i}B_{i+1})\ =\ -2(A_i(B_i-B_{i+1})+A_{i+1}(B_{i+1}-B_i))\ =\ -2((A_i-A_{i+1})(B_i-B_{i+1}))$
因为 $A_i<A_{i+1}$ ,$B_{i}<B_{i+1}$,所以上式小于 $0$
所以按从小到大一一对应是最优的
然后考虑交换次数怎么求,显然 $B$ 的某个交换 $A$ 都有对应的交换,两者等价
所以只要考虑 $A$ 怎么交换就行。每个火柴高度各不相同,确定了 $A$ 和 $B$ 的唯一的对应关系,所以我们知道每个 $A_{i}$ 最终的位置
设初始位置为 $i$ 的火柴最终要到达位置 $C_i$,那么我们可以发现 $C_i$ 是一个 $1$ 到 $n$ 的排列
而且最终交换完成后,$C_i$ 会变成 $1,2,3,...,n$ 的样子
考虑用最少的步数把 $C_i$ 排序,显然这个就是答案,可以证明最小步数等于初始时 $C_i$ 的逆序对数量
证明如下:
考虑一次交换,它要么减少 $0$ 逆序对,要么减少 $1$ 逆序对,而最终状态的逆序对数量为 $0$(也是唯一逆序对为 $0$ 的状态)
考虑冒泡排序的过程,每次选择 $i<j$ 且 $C_i>C_j$ 的数对进行交换,这样最终一定可以达到最终状态
而且发现冒泡排序时,每次交换都会减少 $1$ 逆序对,所以我们交换时就可以模拟冒泡排序,达到最终状态
综上,每次交换逆序对最多减少 $1$,且总有交换使得逆序对减少 $1$,所以逆序对数量就是最少交换次数
我们可以用树状数组求逆序对数量
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e6+7,mo=99999997; int n,c[N]; struct dat{ int v,id; inline bool operator < (const dat &tmp) const { return v<tmp.v; } }a[N],b[N]; int t[N]; ll ans; inline void add(int x) { while(x<=n) t[x]++,x+=x&-x; } inline int ask(int x) { int res=0; while(x) res+=t[x],x-=x&-x; return res; } int main() { n=read(); for(int i=1;i<=n;i++) a[i].v=read(),a[i].id=i; for(int i=1;i<=n;i++) b[i].v=read(),b[i].id=i; sort(a+1,a+n+1); sort(b+1,b+n+1); for(int i=1;i<=n;i++) c[a[i].id]=b[i].id; for(int i=n;i;i--) ans+=ask(c[i]),add(c[i]); printf("%lld\n",ans%mo); return 0; }