zoukankan      html  css  js  c++  java
  • 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
    ",ans%mo);
        return 0;
    }
  • 相关阅读:
    Consul注销实例
    sql优化基础篇
    linux下执行java类(运行java定时器)
    ExecutorService 的理解与使用
    精度计算的方法
    内部类详解
    接口的作用
    面向对象之继承和组合浅谈
    构造器前篇
    教师编制考试数据分析
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11367764.html
Copyright © 2011-2022 走看看