解题思路:尝试使用了比树状数组功能更强大的线段树,线段树可以解决所有用树状数组解决的题,唯一缺点就是需要开辟4*n的大小才能保证不溢出。
注意事项:
对于本题来说,即求数组中某数的逆序对,然后求其等差数列的和即可。本代码没有构造权值线段树的过程,而是直接进行update操作,免去了建树的时间。需要注意的是,需要对给定数组进行两次遍历,一次得到前置逆序,一次得到后置逆序,最后拼接结果即可。

看到有佬还是用了lazy标记让我很不解。。为什么对这样单点修改更新的操作还需要延迟呢?

总的来说,本题算是一道树状数组和线段树的模板题
参考代码:

import java.io.*;

// 蓝桥杯历届试题-小朋友排队 - C语言网 (dotcpp.com)
public class Main {
    static StreamTokenizer cin;
    static PrintWriter out;
    static int n; // 小朋友的个数
    static int[] arr;  // 小朋友身高
    static long[] cnt; // 每个小朋友最少需要交换的次数
    static int[] treeArr;
    static int maxN = -1;  // 存储上值域
    public static void main(String[] args) throws IOException {
        cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        out = new PrintWriter(new OutputStreamWriter(System.out));
        n = nextInt();
        arr = new int[n+1];
        cnt = new long[n+1];
        for(int i = 1; i < n+1; i++){
            int num = nextInt();
            arr[i] = num;
            maxN = Math.max(maxN, num);
        }
        treeArr = new int[(maxN << 2)+10];
        for(int i = 1; i < n+1; i++){
            update(0, 0, maxN, arr[i]);
            // 存储i左边比arr[i]大的元素
            cnt[i] += query(0, 0, maxN, arr[i]+1, maxN);
        }
        // 重构
        treeArr = new int[4*maxN +10];
        for(int i = n; i > 0; i--){
            update(0, 0, maxN, arr[i]);
            // 存储i右边比arr[i]小的元素
            cnt[i] += query(0, 0, maxN, 0, arr[i]-1);
        }
        long res = 0;
        for(int i = 1; i < n+1; i++){
            res += cnt[i]*(cnt[i] + 1)/2;
        }
        out.println(res);
        out.flush();

    }

    // 单点修改, 不需要lazy标记, L和R为此节点值域范围
    static void update(int node, int L, int R, int value){
        if(L == value && R == value){  // 叶子
            treeArr[node]++;  // 重复值++
            return;
        }
        int mid = (L+R) >> 1;
        int leftNode = (node << 1) + 1;
        int rightNode = leftNode + 1;
        if(value = L)
            update(leftNode, L, mid, value);  // 更新左子树
        if(value = mid+1)
            update(rightNode, mid+1, R, value);  // 更新右子树
        treeArr[node] = treeArr[leftNode] + treeArr[rightNode];
    }

    // 查询[value, maxN]间的元素个数
    static long query(int node, int L, int R, int QL, int QR){
        if(QL > R  || L > QR)
            return 0;
        if(QL = R)  // 完全覆盖
            return treeArr[node];
        int mid = (L + R) >> 1;
        int leftNode = (node << 1) + 1;
        int rightNode = leftNode + 1;
        long result = 0;
        if(!(QL > mid))
            result += query(leftNode, L, mid, QL, QR);
        if(!(QR < mid+1))
            result += query(rightNode, mid+1, R, QL, QR);
        return result;
    }

    static int nextInt() throws IOException{
        cin.nextToken();
        return (int)cin.nval;
    }
}


点赞(0)
 

0.0分

1 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论