たのしい家庭菜園【树状数组】【离散化】

>Link

luogu AT1218


>Description

给出一个长度为 n n n 的序列,每次可以交换相邻的两个数,问最少交换多少次使得序列变为一个单峰序列


>解题思路

从小到大枚举每一个数,判断放在左边还是右边
放在左/右边,这个数就要“跨过”它原本左/右边比它大的数,我们就加上它左/右边比它大的数的个数
我们可以手玩一下证明,这样做是最优的。如果一个数 A A A要放到左边去,原本它左边有比它小的数 B B B也要放在左边,乍一看我们好像忽略了 B B B A A A交换的次数,但其实 B B B肯定是在 A A A之前就交换过去了,不会与 A A A相遇
这样做其实也是一种「把代价放在小的数上」,避免重复计算
用树状数组处理比它大的数的数量,需要离散化预处理一下


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300010
#define LL long long
using namespace std;
 
struct node
{
    
    
    int val, l, r;
} a[N];
int n, t[N], m, q[N];
LL ans;
 
void add (int x)
{
    
    
    for (; x; x -= x & (-x))
      t[x]++;
}
int ask (int x)
{
    
    
    int ret = 0;
    for (; x <= n; x += x & (-x))
      ret += t[x];
    return ret;
}
bool cmp (node aa, node bb) {
    
    return aa.val < bb.val;}
 
int main()
{
    
    
    scanf ("%d", &n);
    for (int i = 1; i <= n; i++)
	  scanf ("%d", &a[i].val), q[++m] = a[i].val;
	sort (q + 1, q + 1 + m);
	m = unique (q + 1, q + 1 + m) - q - 1;
	for (int i = 1; i <= n; i++)
	  a[i].val = lower_bound (q + 1, q + 1 + m, a[i].val) - q;
    for (int i = 1; i <= n; i++)
    {
    
    
        a[i].l = ask (a[i].val + 1);
        add (a[i].val);
    }
    memset (t, 0, sizeof (t));
    for (int i = n; i >= 1; i--)
    {
    
    
        a[i].r = ask (a[i].val + 1);
        add (a[i].val);
    }
    sort (a + 1, a + 1 + n, cmp);
    for (int i = 1; i <= n; i++)
      ans += min ((LL)a[i].l, (LL)a[i].r);
    printf ("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/121276625