蓝桥杯 小朋友排队 (树状数组)

本文探讨了一个经典的小朋友排队问题,通过树状数组高效地解决了计算最小不高兴程度和的难题。介绍了树状数组的基本原理,如何利用其解决前缀和问题,并通过实例详细解释了算法的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小朋友排队  

时间限制:1.0s   内存限制:256.0MB

问题描述

  n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
  每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
  如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
  请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
  如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

输入格式

  输入的第一行包含一个整数n,表示小朋友的个数。
  第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。

输出格式

  输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

样例输入

3
3 2 1

样例输出

9

样例说明

  首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

数据规模和约定

  对于10%的数据, 1<=n<=10;
  对于30%的数据, 1<=n<=1000;
  对于50%的数据, 1<=n<=10000;
  对于100%的数据,1<=n<=100000,0<=Hi<=1000000。

易知对于一个小朋友而言,知道左边比他高的,知道右边比他矮的,就是他需要移动的次数。维护一个次数的等差数列的前缀和就是答案。

树状数组能解决前面有多少个数比他小的问题,那么前i个数,有x个比他小,比他大的数就是i-1-x,i-1就是减去数本身。然后将数组倒叙加入到树状数组中,可以求出后面有多少比他小的数,最后将这两部分加起来,就是每一个小朋友应该移动的次数和,再用等差数列求和公式,得到答案。

代码实现:

/*
Look at the star
Look at the shine for U
*/
#include<bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define sl(x) scanf("%lld",&x)
using namespace std;
const int N = 1e6+5;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll c[N];
ll lowbit(ll x)
{
    return x&(-x);
}
void add(ll x)
{
    while(x < N)
    {
        c[x]++;
        x += lowbit(x);
    }
}
ll query(ll x)
{
    ll ans = 0;
    while(x)
    {
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
ll s[N],sum[N];
int main()
{
    ll n,i,j,k,m;
    sl(n);
    ll ans = 0;
    for(i = 0;i < n;i++)
    {
        sl(s[i]);
        add(s[i]+1);
        sum[i] += (i+1-query(s[i]+1));
    }
    memset(c,0,sizeof(c));
    for(i = n-1;i >= 0;i--)
    {
        add(s[i]+1);
        sum[i] += query(s[i]);
    }
    for(i = 0;i < n;i++)
        ans += (sum[i]*sum[i]+sum[i])/2;
    printf("%lld\n",ans);
    return 0;
}

 

<think>好的,我现在需要帮助用户查找蓝桥杯比赛中关于棋盘问题的树状数组解法或实现代码。首先,我要确定用户的需求是什么。用户提到了“棋盘问题”和“树状数组”,可能需要解决的是二维区间操作问题,比如格子修改或查询,这在蓝桥杯的题目中比较常见,比如《棋盘上的问题》或《格子刷油漆》等题目。 首先,我需要回忆树状数组(Fenwick Tree)的基本知识。树状数组通常用于高效处理前缀和查询和单点更新,时间复杂度为O(log n)。但在棋盘问题中,可能涉及到二维的情况,这时候需要使用二维树状数组,可以处理二维区域的操作,比如子矩阵的更新和求和。 接下来,我需要考虑蓝桥杯历届真题中是否有类似的题目。根据引用[1]和[2],用户提供的参考资料可能涉及C++和Python的代码,但具体到棋盘和树状数组的结合,可能需要进一步查找相关题解。例如,有些题目可能要求动态维护棋盘的状态,进行多次区间增减操作,并查询某个区域的和或极值。这时候树状数组的优势就体现出来了。 然后,用户需要解题思路和实现代码。针对二维棋盘问题,树状数组的二维实现是关键。每个节点(i,j)维护的是一个二维的前缀和信息。当进行区间更新时,可能需要使用差分的方法,将区间操作转化为四个单点操作,再利用树状数组的高效更新和查询。 例如,假设题目要求对棋盘的一个子矩阵进行增加某个值,然后查询某个点的值。这时候可以用二维树状数组结合差分的方法。具体来说,对于每个更新操作(x1,y1,x2,y2)加上v,可以通过四次更新操作实现:在(x1,y1)加v,(x1,y2+1)减v,(x2+1,y1)减v,(x2+1,y2+1)加v。这样,查询某个点(x,y)的值时,只需计算前缀和即可得到当前值。 接着,代码实现部分需要构建二维树状数组的结构。在Python中,可以初始化一个二维数组,并实现update和query方法。需要注意的是,树状数组的下标通常从1开始,因此在处理输入数据时可能需要调整坐标。 验证思路的正确性也很重要。例如,考虑一个小棋盘,手动模拟几次更新和查询操作,确保结果符合预期。同时,需要注意边界条件,比如当更新区域超出棋盘范围时的处理。 最后,根据用户的引用资料,引用[1]提到了C++中的unordered_map用于节点答案的存储,可能与本题无关,但引用[2]中的Python处理大数的方法可能在输入时有用,但树状数组的实现本身不需要处理大数,所以主要关注树状数组的结构和操作。 总结步骤: 1. 确定问题类型:二维区间更新和单点查询。 2. 使用二维树状数组结合差分方法处理区间更新。 3. 实现二维树状数组的update和query函数。 4. 编写代码框架,处理输入输出,并测试样例。 可能的挑战包括正确实现二维树状数组的更新和查询逻辑,以及差分方法的正确应用。需要仔细检查每个步骤,避免逻辑错误。</think>关于蓝桥杯比赛中棋盘问题与树状数组的结合应用,以下为典型解题思路及实现代码: --- ### 问题特征 棋盘问题通常涉及**二维区间操作**,例如: - 多次对子矩阵执行增减操作 - 查询单点或子矩阵的值 - 统计符合特定条件的区域 树状数组(Fenwick Tree)通过$O(\log n)$时间复杂度实现高效的前缀和计算与单点更新,适合处理这类高频操作的问题[^1]。 --- ### 解题思路(以子矩阵增减+单点查询为例) 1. **差分思想** 将二维区间增减转化为四个单点操作: $$ \text{update}(x1,y1,+v),\ \text{update}(x1,y2+1,-v) $$ $$ \text{update}(x2+1,y1,-v),\ \text{update}(x2+1,y2+1,+v) $$ 2. **二维树状数组实现** 维护差分数组的前缀和,实现单点修改与区域查询: ```python class Fenwick2D: def __init__(self, n, m): self.n = n self.m = m self.tree = [[0]*(m+1) for _ in range(n+1)] def update(self, x, y, delta): while x <= self.n: y1 = y while y1 <= self.m: self.tree[x][y1] += delta y1 += y1 & -y1 x += x & -x def query(self, x, y): res = 0 while x > 0: y1 = y while y1 > 0: res += self.tree[x][y1] y1 -= y1 & -y1 x -= x & -x return res ``` 3. **操作映射** - **子矩阵更新**:调用四次`update()`实现差分 - **单点查询**:直接计算`query(x,y)` --- ### 完整代码实现(Python) ```python class Fenwick2D: def __init__(self, n, m): self.n = n self.m = m self.tree = [[0]*(m+2) for _ in range(n+2)] # 下标从1开始 def update(self, x, y, delta): while x <= self.n: y1 = y while y1 <= self.m: self.tree[x][y1] += delta y1 += y1 & -y1 x += x & -x def query(self, x, y): res = 0 while x > 0: y1 = y while y1 > 0: res += self.tree[x][y1] y1 -= y1 & -y1 x -= x & -x return res # 输入棋盘大小n x m,操作次数q n, m = map(int, input().split()) q = int(input()) fw = Fenwick2D(n, m) for _ in range(q): op = list(map(int, input().split())) if op[0] == 1: # 子矩阵更新: op1 x1 y1 x2 y2 v x1, y1, x2, y2, v = op[1], op[2], op[3], op[4], op[5] fw.update(x1, y1, v) fw.update(x1, y2+1, -v) fw.update(x2+1, y1, -v) fw.update(x2+1, y2+1, v) else: # 单点查询: op2 x y x, y = op[1], op[2] print(fw.query(x, y)) ``` --- ### 应用场景 1. 蓝桥杯《格子刷油漆》题型中动态维护棋盘状态 2. 统计符合条件(如异或特征)的区域数量 3. 解决多次操作后的棋盘极值问题 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值