2023年GESP六级小杨握手问题(洛谷B3874):Fenwick树求解逆序对的代码解析

贾蔷
• 阅读 8

2023年GESP六级小杨握手问题(洛谷B3874):Fenwick树求解逆序对的代码解析 一、题目解读 “小杨的握手问题”源自2023年GESP六级考试(对应洛谷题目B3874)。题目描述为:给定一个长度为N的排列,每次将当前数与之前未访问过的数握手,求总共握手次数。本质上是求排列中逆序对的个数,即统计每个数右侧比它小的元素数量。需设计高效算法在O(NlogN)时间内解决。

二、解题思路 核心思想是将握手次数转化为逆序对计数问题,利用Fenwick树(二叉索引树)实现动态区间查询与更新。通过维护数组元素的顺序统计信息,每次查询当前数之前已出现数的个数,即为该数的握手次数。关键在于将离散的握手操作转化为可差分维护的区间操作,避免暴力枚举。

三、解题步骤

  1. 数据预处理:将输入数据转换为1-based索引(原代码中通过order[i]++实现),便于Fenwick树操作。

  2. 构建Fenwick树:初始化大小为N的树,初始值全为0。

  3. 逆序对统计:

    1.遍历排列顺序,对于每个数current:

    2.查询ft.query(current-1):获取当前位置前比current小的元素个数(即左侧逆序对)。

    3.更新ft.update(current,1):将current加入树中,标记已访问。

  4. 累计握手次数:将每次查询结果累加至handshakes变量。

  5. 输出结果:最终handshakes即为总握手次数。

四、代码与注释

#include <iostream>
#include <vector>
using namespace std;

class FenwickTree {
private:
   vector<int> tree;
   int size;
public:
   FenwickTree(int n) : size(n), tree(n + 1, 0) {} // 构造函数初始化树大小及全0数组

   void update(int index, int delta) {
       for(; index <= size; index += index & -index) // 从index到末尾更新,利用二进制位操作
           tree[index] += delta;
   }

   int query(int index) {
       int res = 0;
       for(; index > 0; index -= index & -index) // 从index到1查询,逐步累加
           res += tree[index];
       return res;
   }
};

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr); // 加快输入输出速度

   int N;
   cin >> N;
   vector<int> order(N);
   for(int i = 0; i < N; i++) {
       cin >> order[i];
       order[i]++; // 转换为1-based索引,适配Fenwick树操作
   }

   FenwickTree ft(N);
   long long handshakes = 0;

   for(int i = 0; i < N; i++) {
       int current = order[i];
       handshakes += ft.query(current - 1); // 查询比current小的已存在数个数
       ft.update(current, 1); // 标记当前数已出现
   }

   cout << handshakes << endl;
   return 0;
}

五、总结 本解法通过将握手问题转化为逆序对计数,利用Fenwick树的O(logN)区间查询与更新,实现高效求解。相较于暴力或归并排序等算法,Fenwick树具有代码简洁、常数优化的优势,尤其在处理动态统计问题时表现优异。掌握此类数据结构的应用,对算法竞赛与实际问题求解具有重要意义。 原文:2023年GESP六级小杨握手问题(洛谷B3874):Fenwick树求解逆序对的代码解析

点赞
收藏
评论区
推荐文章
DaLongggggg DaLongggggg
4年前
python-算法训练 区间k大数查询
问题描述给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。输入格式第一行包含一个数n,表示序列长度。第二行包含n个正整数,表示给定的序列。第三个包含一个正整数m,表示询问个数。接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。输出格式总共输出m行,每行一个数
Karen110 Karen110
4年前
人工智能数学基础-线性代数5:行列式求解线性方程组和拉普拉斯定理
一、逆序及逆序数在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的实际先后次序与标准次序不同时,就说有1个逆序
Wesley13 Wesley13
3年前
n级排列
n级排列由1,2,...,n组成的一个有序数组称为一个n级排列。例如,2431是一个四级排列,45321是一个五级排列。注:n级排列的总数是n(n1)(n2)...1n!逆序在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序逆序数一个排列中逆序的总数
深度学习 深度学习
5小时前
【CSP-S 2019】括号树(洛谷P5658):栈+DFS
一、题目解读括号树问题(洛谷P5658)要求处理一个由括号序列转化的树结构:每个节点表示一个括号,'('为子节点,')'为父节点。题目给定一棵n个节点的树,需计算每个节点的深度(括号层数),并输出所有节点深度与节点编号乘积的异或和。核心在于将括号序列转化为
深度学习 深度学习
5小时前
洛谷1111题解:基于Kruskal算法与并查集的最小生成树实现
一、题目解读洛谷1111题是一道经典的图论问题,要求构建一个无向图的最小生成树,并输出其最大边权值。题目核心在于通过给定的边集合,找到连接所有节点的最小权值子集,同时保证无环。这通常涉及最小生成树算法(如Kruskal)的应用,需要高效处理边权重与节点连通
深度学习 深度学习
5小时前
2024蓝桥杯省赛B组前缀总分(洛谷P12124)解题思路与代码详解
一、题目解读2024年蓝桥杯省B组题目“前缀总分”(对应洛谷P12124)要求计算给定字符串集合中,所有前缀的最长公共前缀(LCP)的总分,并找出通过移动字符位置后可能获得的最大总分。题目考察字符串处理与动态规划能力,需高效计算LCP并优化得分策略。二、解
贾蔷 贾蔷
5小时前
2023年GESP六级题解:洛谷P10108闯关游戏动态规划解法详解
一、题目解读本文针对2023年GESP六级题目“闯关游戏”(洛谷P10108)进行详细解析。题目要求玩家通过不同关卡路径选择,计算从起点到终点的最大得分。关卡间存在跳跃规则,需结合动态规划思想设计高效算法,最终输出最优得分。二、解题思路采用动态规划(Dyn
深度学习 深度学习
5小时前
洛谷P2034题解:动态规划+单调队列优化求解最大K段子段和问题
一、题目解读洛谷P2034题目要求给定一个长度为n的整数数组,将其分成不超过k段,求各段和的最大值。该问题属于经典动态规划问题的扩展,需结合优化技巧高效求解。二、解题思路采用动态规划单调队列优化的策略。核心思想是定义状态dp
贾蔷 贾蔷
2星期前
洛谷P1255题 解题思路和步骤 C++实现带注释,c++入门基础题
一、问题描述与递推关系建立洛谷P1255数楼梯问题要求计算n级台阶的不同走法数,每次可以跨1级或2级。这本质上是斐波那契数列的变种问题,递推公式为f(n)f(n1)f(n2)。当n≤50时可用普通整型存储,但题目中n可能达到5000,这就必须使用高精度运
贾蔷 贾蔷
2星期前
2025年GESP七级等价消除(洛谷P11965)代码解析与优化策略
一、题目解读2025年GESP七级考试中的“等价消除(洛谷P11965)”问题要求统计给定字符串中满足等价条件的子串数量。所谓“等价子串”,是指子串中所有字符出现的次数均相同。题目需要高效算法解决,考验对字符串处理和状态压缩的掌握。二、解题思路采用位运算