zoukankan      html  css  js  c++  java
  • 【洛谷1966】火柴排队(逆序对)

    点此看题面

    大致题意:(a)(b)两个数组,问你要将(b)进行多少次相邻两数交换操作,才能使得(sum_{i=1}^n(a_i-b_i)^2)最小。

    转化题意

    不难猜到,当(a)中第(i)小的数与(b)中第(i)小的数配对时,值最小。

    证明以(n=2)为例((n>2)同理),设(a_1<a_2,b_1<b_2),此时只有两种匹配方式:

    1. (S_1=(a_1-b_1)^2+(a_2-b_2)^2=a_1^2+a_2^2+b_1^2+b_2^2-2a_1b_1-2a_2b_2)
    2. (S_2=(a_1-b_2)^2+(a_2-b_1)^2=a_1^2+a_2^2+b_1^2+b_2^2-2a_1b_2-2a_2b_1)

    则用(S_1)(S_2)得:

    [S_1-S_2=-2(a_1b_1+a_2b_2-a_1b_2-a_2b_1)=-2(a_2-a_1)(b_2-b_1) ]

    [ecause a_1<a_2,b_1<b_2, herefore S_1-S_2<0, herefore S_1<S_2. ]

    原命题得证。

    (rb_i)(b_i)(b)序列中第几小的数,则我们可以得出一个序列(s),其中(s_i)表示(a)序列中第(rb_i)小的数的位置

    那么题意就变成了求要对(s)序列进行多少次冒泡排序操作,才可以使(s)有序。

    逆序对

    根据一个著名定理,冒泡排序操作次数即为逆序对个数。

    因此我们直接逆序对做即可。

    我采用的是归并排序,具体实现见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define X 99999997
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    using namespace std;
    int n,s[N+5];
    struct data
    {
    	int p,v;I data(CI x=0,CI y=0):p(x),v(y){}
    	I bool operator < (Con data& o) Con {return v^o.v?v<o.v:p<o.p;}
    }a[N+5],b[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class MergeSolver//归并排序
    {
    	private:
    		int tot,p[N+5];
    		I void Merge(CI l,CI r)
    		{
    			if(l>=r) return;RI mid=l+r>>1;Merge(l,mid),Merge(mid+1,r);
    			RI i=l,j=mid+1,k=l;W(i<=mid&&j<=r)
    				s[i]<=s[j]?p[k++]=s[i++]:(p[k++]=s[j++],Inc(tot,mid-i+1));//注意此处统计逆序对
    			W(i<=mid) p[k++]=s[i++];W(j<=r) p[k++]=s[j++];
    			for(i=l;i<=r;++i) s[i]=p[i];
    		}
    	public:
    		I void Solve() {Merge(1,n),printf("%d",tot);}
    }M;
    int main()
    {
    	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i].v),a[i].p=i;sort(a+1,a+n+1);
    	for(i=1;i<=n;++i) F.read(b[i].v),b[i].p=i;sort(b+1,b+n+1);
    	for(i=1;i<=n;++i) s[b[i].p]=a[i].p;return M.Solve(),0;//求出序列s
    }
    
  • 相关阅读:
    Android开发笔记——WebView
    字符串_最小表示法求循环串的最小序列(HDU_4162)
    STL_map简单应用(HDU_1075)
    DP_最大子序列和(HDU_1003)
    STL map 使用方法(转)
    数学_线性筛法建立素数表(HDU_1262)
    學習筆記 ADO數據庫訪問技術
    C#多线程学习
    Java容器
    选取单元格的基本语句
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu1966.html
Copyright © 2011-2022 走看看