P1966 火柴排队 - 逆序对

传送门

题目要求最小化∑(ai-bi)^2,即对于每一个i,都有序列A中的第i大对应序列B中的第i大,问题就转化为如何用最小次数实现这一情况 。
首先对序列A和B进行离散化,记录每个数的位置,然后排序,得到两个有序序列。
然后,我们可新建一个序列C,其中C[A[i].pos]=B[i].pos,形成序列A与B的对应关系,移动后最终C[i]=i,此时相当于A[i].pos=B[i].pos,达到了目标情况。
而使序列C成为单调上升序列的移动次数即为序列C中的逆序对数,可通过树状数组或归并排序等求解。
updata_1_2018.5.29:注意不是离散化,而是记录每个数所在的位置,并将两个序列排序后形成两个第k大数的位置的对应关系。(而不是“相同位置离散化后的数值的对应关系”)

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100000+100,p=99999997;
typedef long long ll;
int n;
struct node{
    ll val;
    int pos;
};
node a[N],b[N];
bool cmp(node a,node b){
    return a.val<b.val;
}
int c[N];
int tree[N];
int lowbit(int x){
    return x&-x;
}
void add(int x,int k){
    for(;x<=n;x+=lowbit(x)){
        tree[x]+=k;
        tree[x]%=p;
    }
}
int ask(int x){
    int ans=0;
    for(;x;x-=lowbit(x)){
        ans+=tree[x];
        ans%=p;
    }
    return ans;
}
void nixud_treearray(){
    int ans=0;
    for(int i=n;i;i--){
        add(c[i],1);
        ans+=ask(c[i]-1);
        ans%=p;
    }
    printf("%d",ans);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i].val);
        a[i].pos=i;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        scanf("%lld",&b[i].val);
        b[i].pos=i;
    }
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;i++) c[a[i].pos]=b[i].pos;
    nixud_treearray();
    return 0;
}

 

posted @ 2018-04-05 16:04  dprswdr  阅读(162)  评论(0)    收藏  举报