火柴排队
传送门 https://www.luogu.org/problemnew/show/P1966
欲求min∑(ai-bi)^2,我们可以先将它展开,得到min∑(ai^2-2*ai*bi+bi^2),ai^2与bi^2的值是一定的,因此只需考虑min∑ai*bi(系数2也忽略)。
不难发现这样一条性质:当序列a中的数与序列b中的数处在相同位置,且它们在各自序列中相对大小相同时,得到的ai*bi最优。
证明:详见洛谷题解(逃。
那么具体如何操作呢,众所周知,这是来自gg给的PPT里在归并排序下的题,所以这道题一定离不开归并排序(其实还可以用树状数组。
为了得到满足上述性质的两个序列,可以利用下标的单调性。
2 3 1 4 3 2 1 4 ------> 1 2 3 4 1 2 3 4 ------> c[3] = 3,c[1] = 2,c[2] = 1,c[4]=4.
1 2 3 4 1 2 3 4 3 1 2 4 3 2 1 4
c[b[i].num] = a[i].num,c下标与它的值所“差”的地方,就是我们需要在a或b序列中调整的地方,对c进行归并排序求逆序对即可。
附上只有80pts的代码
#include<cstdio> #include<algorithm> using namespace std; const int mod = 99999997; const int N = 1e5+1; inline int read() { static char ch; while((ch = getchar()) < '0' || ch > '9'); int ret = ch - 48; while((ch = getchar()) >= '0' && ch <= '9') ret = ret * 10 + ch - 48; return ret; } struct node { int x,num; }a[N],b[N]; int n,t[N],c[N]; long long int ans; void Merge(int l,int m,int r) { int i = l,k = l,j = m+1; while(i <= m && j <= r) { if(c[i] > c[j]) { t[k++] = c[j++]; ans += (m-i+1)%mod; } else t[k++] = c[i++]; } while(i <= m) t[k++] = c[i++]; while(j <= r) t[k++] = c[j++]; for(int i = l;i <= r;i++) c[i] = t[i]; } void Merge_sort(int l,int r) { if(l < r) { int mid = (l+r)>>1; Merge_sort(l,mid); Merge_sort(mid+1,r); Merge(l,mid,r); } } bool cmp(node p,node q){return p.x < q.x;} int main() { n = read(); for(int i = 1;i <= n;i++) { a[i].x = read(); a[i].num = i; } for(int i = 1;i <= n;i++) { b[i].x = read(); b[i].num = i; } sort(a+1,a+n+1,cmp); sort(b+1,b+n+1,cmp); for(int i = 1;i <= n;i++) c[b[i].num] = a[i].num; Merge_sort(1,n); printf("%lld",ans); return 0; }