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; }

浙公网安备 33010602011771号