CF1579E2 题解

CF1579 E2 题解

题意大意:

  • 有一个双端队列,开始为空。
  • 给出 n n n 个数,要在队列的前方或后方插入。
  • 问最后队列的逆序对最小是多少

不难发现一个贪心策略:在哪里插入新增的逆序对更少就往哪里插

证明如下:

  • 考虑在某一个点中,选择逆序对更多的插入。
  • 因为后面的数是在前面和末尾插入的,所以不影响后面的任何逆序对数量。
  • 而此处逆序对只能更多,所以原策略最优。

求新增逆序对数目很简单。

扫一下队列里的数,如果 a i < x a_i < x ai<x 那么插入在前面的逆序对数量增加,反之亦然。

O ( n 2 ) O(n^2) O(n2),显然 TLE。于是想到了用 multiset 做。

接着,我用了 multiset 来交,结果光荣地 CE 了。

原来,STL 中不支持任何一个容器,使得可以 O ( 1 ) O(1) O(1) 插入, O ( 1 ) O(1) O(1) 查询

于是,有更好的办法吗?

想当年刚学逆序对时,用到的树状数组正好可以满足条件。

建两个树状数组,一个正的,一个反的。在加的过程中,像之前一样查找就行了。

注意要离散化,不能开 memset,TLE 见祖宗!

离散化时要注意相同的情况。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
int s[1000001], s1[1000001]; // 两个树状数组
inline int lowbit(int x){
    return x & (-x);
}
void add(int k, int x){
	// 分两次
    int t = k;
    while(k <= n){
        s[k] += x;
        k += lowbit(k);
    }
    k = n - t + 1;
    while(k <= n){
        s1[k] += x;
        k += lowbit(k);
    }
}
pair<int, int> query(int x){
	// 依然分两次
    int ans = 0;
    int t = x;
    x--;
    while(x > 0){
        ans += s[x];
        x -= lowbit(x);
    }
    x = n - t + 1;
    x--;
    int ans1 = 0;
    while(x > 0){
        ans1 += s1[x];
        x -= lowbit(x);
    }
    return make_pair(ans, ans1);
}
signed main(){
    int t;
    cin >> t;
    while(t--){
        set<int> mts;
        int ans = 0;
        cin >> n;
        pair<int, int> a[n];
        int b[n];
        // 不开 memset 见祖宗
        for(int i = 1;i <= n;i++){
            s[i] = 0, s1[i] = 0;
        }
        for(int i = 0;i < n;i++){
            cin >> a[i].first;
            a[i].second = i;
        }
        sort(a, a + n);
        // 离散化
        for(int i = 0;i < n;i++){
            if(i == 0)
                b[a[i].second] = 1;
            else if(a[i - 1].first != a[i].first)
                b[a[i].second] = b[a[i - 1].second] + 1;
            else
                b[a[i].second] = b[a[i - 1].second];
        }
        // 普通的求逆序对
        for(int i = 0;i < n;i++){
            pair<int, int> p = query(b[i]);
            add(b[i], 1);
            ans += min(p.first, p.second);
        }
        cout << ans << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值