1. 程式人生 > 實用技巧 >NOIP2013 火柴排隊 題解

NOIP2013 火柴排隊 題解

題解


首先的話,上個題目連結 https://www.luogu.com.cn/problem/P1966

讀懂了題目大意,稍微有點頭緒

我們發現要求這個和的最小值

即min{∑(ai-bi)^2 (1<=i<=n)}

展開,得min{∑(ai^2+bi^2-2*ai*bi)}=min{∑ai^2+∑bi^2-∑2*ai*bi}

只需要關注2*Σ(a[i]*b[i])的值,使它最大就行了(因為a的平方+b的平方始終為定值)

然後的話,這時候需要證明一下這個東西

讓b陣列中第i小的數和a陣列中第i小的數在同一個位置是最優的

順序之乘>=亂序之乘

證明如下:

我們可以設a<b,c<d

猜測ac+bd一定是最大的。利用反證法。

若ac+bd不是最大的,那麼一定有比它更大的,只有ad+bc

ac+bd<ad+bc

ac-ad<bc-bd

a*(c-d)<b*(c-d)//c-d<0

那麼就會得到a>b (什麼鬼,矛盾,所以得證)

所以我們知道

對於有序數列k1~kn,p1~pn,

k(1)p(1)+k(2)p(2)+……+k(n)p(n)一定是最大的

於是這題變成了,我們要用最少多少次使得這兩個序列變成所謂b陣列中第i小的數和a陣列中第i小的數在同一個位置

我們用一個很巧妙的辦法,設輸入的序列分別為a,b;

開一個數組c,令c[b[i]]=a[i] (這時候如果c[i]=[i],他們就在同一個位置)

於是我們只要愉快的求一下c陣列的逆序對對數就是最終的answer!

求逆序對,樹狀陣列?

像我這樣的蒟蒻當然懶得寫,二分歸併不好嘛。

於是A了一道很水的洛谷藍題(?)

code如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=99999997;
ll n,p[100010],x[100010],ans=0;
struct node
{
    int data,position;
}a[100010],b[100010];
bool cmp1(node a,node b)
{
    return
a.data<b.data; } inline ll read() { char ch; ll res,sign=1; while((ch=getchar())<'0'||ch>'9') if(ch=='-') sign=-1; res=ch^48; while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^48); return res*sign;//快讀卡常,不過這題沒用 } inline void msort(int s,int t) { if(s==t)return ; int mid=(s+t)/2; msort(s,mid);msort(mid+1,t); int i=s,k=s,j=mid+1; while(i<=mid && j<=t) { if(x[i]<=x[j]) { p[k]=x[i]; ++k;++i; } else { p[k]=x[j]; ++k;++j; ans=(ans+mid-i+1)%mod; //ans要增加左邊剩餘區間的個數,用來統計逆序對 } } while(i<=mid) { p[k]=x[i]; ++k;++i; } while(j<=t) { p[k]=x[j]; ++k;++j; } for(int i=s;i<=t;i++) { x[i]=p[i]; } } int main() { n=read(); for(int i=1;i<=n;i++) scanf("%d",&a[i].data),a[i].position=i; for(int i=1;i<=n;i++) scanf("%d",&b[i].data),b[i].position=i; sort(a+1,a+n+1,cmp1); sort(b+1,b+n+1,cmp1); for(register int i=1;i<=n;++i) { x[b[i].position]=a[i].position; }//記錄位置 msort(1,n);//歸併排序求逆序對 printf("%lld",ans); return 0; }