逆序对--P1966 火柴排队
*题意:两个数组$a$和$b$,使$\sum_{i=1}^n {(a_i-b_i)}^2$ 最小
*思路:对于上述的完全平方公式,展开后变成$\sum_{i=1}^n {a_i}^2+{b_i}^2-2a_ib_i$,其中前两项为定值,我们继续变化$\sum_{i=1}^n {a_i}^2+{b_i}^2$-2$\sum_{i=1}^n a_ib_i$
只要令$\sum_{i=1}^n a_ib_i$越大越好,由此我们引入一个结论顺序之乘>=乱序之乘
*证明:设有序数列$x$,$y$,即有$x_1y_1+x_2y_2$>$x_1y_2+x_2y_1$
变形一下可得$x_1y_1-x_2y_1$>$x_1y_2-x_2y_2$ $\to$ $y_1(x_1-x_2)$>$y_2(x_1-x_2)$,因为$y_1$>$y_2$,第一个式子成立
由1及2,再到$n$,得出结论顺序之乘>=乱序之乘,甚至还有结论:同序和$\ge$乱序和$\ge$逆序和
*代码实现:让$a$,$b$数组全部升序排序,以$a_i$为关键字,对$b$进行排序,也就是说,$a$的最大对应$b$的最大,$a$的次大对应$b$的次大,$x_i$表示在$b$中的位置,$i$,表示的是在$a$中的位置,我们想让$a$与$b$序列相等,就希望$x$数组升序,所以答案就变成了求$x$数组的逆序对数
完整代码:
1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 using namespace std;
5 const int maxn=1e6+10,mod=1e8-3;
6 int n;
7 int q[maxn],sum[maxn];
8 struct node{
9 int val,pos;
10 }a[maxn],b[maxn];
11 bool cmp(node x,node y){
12 return x.val<y.val;
13 }
14 void fix(int x,int k){
15 for (int i = x;i <= n;i+=i&(-i)) sum[i]+=k;
16 }
17 int query(int x){
18 int res=0;
19 for (int i = x;i >= 1;i-=i&(-i)) res+=sum[i];
20 return res;
21 }
22 int main(){
23 scanf ("%d",&n);
24 for (int i = 1;i <= n;i++) {scanf ("%d",&a[i].val);a[i].pos=i;}
25 for (int i = 1;i <= n;i++) {scanf ("%d",&b[i].val);b[i].pos=i;}
26 sort(a+1,a+n+1,cmp);sort(b+1,b+n+1,cmp);
27 for (int i = 1;i <= n;i++) q[a[i].pos]=b[i].pos;
28 int ans=0;
29 for (int i = n;i >= 1;i--){
30 fix(q[i],1);
31 ans+=query(q[i]-1);
32 ans%=mod;
33 }
34 printf("%d\n",ans);
35 return 0;
36 }