火柴排队

传送门 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;
} 

 

posted @ 2018-10-22 17:29  我的露娜不会飘  阅读(142)  评论(0编辑  收藏  举报