NOIP2013 火柴排隊 題解
阿新 • • 發佈:2020-11-04
題解
首先的話,上個題目連結 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) { returna.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; }