逆序对--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 }

 

posted @ 2020-10-10 07:42  小又又  阅读(77)  评论(0编辑  收藏  举报