P1966 火柴排队

时间:2019-08-17
本文章向大家介绍P1966 火柴排队,主要包括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$,所以逆序对数量就是最少交换次数

我们可以用树状数组求逆序对数量

#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;
}

原文地址:https://www.cnblogs.com/LLTYYC/p/11367764.html