题解:AcWing 241 楼兰图腾

【题目来源】

AcWing:241. 楼兰图腾 - AcWing题库

【题目描述】

在完成了分配任务之后,西部 \(314\) 来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(),他们分别用 V 的形状来代表各自部落的图腾。

西部 \(314\) 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 \(n\) 个点,经测量发现这 \(n\) 个点的水平位置和竖直位置是两两不同的。

西部 \(314\) 认为这幅壁画所包含的信息与这 \(n\) 个点的相对位置有关,因此不妨设坐标分别为 \((1,y_1),(2,y_2),\dots,(n,y_n)\),其中 \(y_1\sim y_n\)\(1\)\(n\) 的一个排列。

西部 \(314\) 打算研究这幅壁画中包含着多少个图腾。

如果三个点 \((i,y_i),(j,y_j),(k,y_k)\) 满足 \(1\le i\lt j\lt k\le n\)\(y_i>y_j,y_j<y_k\),则称这三个点构成 V 图腾;

如果三个点 \((i,y_i),(j,y_j),(k,y_k)\) 满足 \(1\le i\lt j\lt k\le n\)\(y_i<y_j,y_j>y_k\),则称这三个点构成 图腾;

西部 \(314\) 想知道,这 \(n\) 个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和 的个数。

【输入】

第一行一个数 \(n\)

第二行是 \(n\) 个数,分别代表 \(y_1,y_2,\dots,y_n\)

【输出】

两个数,中间用空格隔开,依次为 V 的个数和 的个数。

【输入样例】

5
1 5 3 2 4

【输出样例】

3 4

【解题思路】

image

【算法标签】

《AcWing 241 楼兰图腾》 #树状数组# #线段树# #分块#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 使用long long类型
const int N = 200005;  // 定义数组最大长度

int n;                // 数组长度
int a[N];             // 存储原始数组
int tr[N];            // 树状数组
int Greater[N];       // 存储每个元素右边比它大的元素个数
int Lower[N];         // 存储每个元素右边比它小的元素个数

// 计算x的最低有效位(用于树状数组索引计算)
int lowbit(int x)
{
    return x & -x;
}

// 向树状数组添加值:在位置x增加c
void add(int x, int c)
{
    // 从x开始向上更新所有相关节点
    for (int i = x; i <= n; i += lowbit(i))
    {
        tr[i] += c;
    }
}

// 查询前缀和:计算前x个元素的和
int query(int x)
{
    int res = 0;
    // 从x开始向下累加所有相关节点
    for (int i = x; i; i -= lowbit(i))
    {
        res += tr[i];
    }
    return res;
}

signed main()
{
    // 输入数组长度n
    cin >> n;
    
    // 输入原始数组
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    
    // 第一遍扫描:从左到右计算每个元素左边的情况
    for (int i = 1; i <= n; i++)
    {
        int y = a[i];
        // 计算当前元素右边比它大的元素个数
        Greater[i] = query(n) - query(y);
        // 计算当前元素右边比它小的元素个数
        Lower[i] = query(y - 1);
        // 将当前元素加入树状数组
        add(y, 1);
    }
    
    // 重置树状数组
    memset(tr, 0, sizeof(tr));
    
    int res1 = 0, res2 = 0;  // res1-逆序对总数,res2-顺序对总数
    
    // 第二遍扫描:从右到左计算结果
    for (int i = n; i; i--)
    {
        int y = a[i];
        // 计算逆序对总数
        res1 += Greater[i] * (query(n) - query(y));
        // 计算顺序对总数
        res2 += Lower[i] * (query(y - 1));
        // 将当前元素加入树状数组
        add(y, 1);
    }
    
    // 输出结果:逆序对总数和顺序对总数
    cout << res1 << " " << res2 << endl;
    
    return 0;
}

【运行结果】

5
1 5 3 2 4
3 4
posted @ 2026-02-24 10:50  团爸讲算法  阅读(1)  评论(0)    收藏  举报