蓝桥杯 无影之谜

原题目链接

问题描述

在 "无影之谜" 解密大赛中,有 N 位选手,每位选手有一个影响力值 S[i]。选手 j 会投票给选手 i,当且仅当选手 j 的影响力值大于或等于他们之间(不包括选手 ij)其他选手的影响力值的总和。

输入格式

  • 第一行包含一个整数 N,表示选手的数量。
  • 第二行包含 N 个空格分隔的整数 S[1], S[2], ..., S[N],表示每个选手的影响力值。

输出格式

  • 输出一行包含 N 个空格分隔的整数,表示每个选手得到的投票数。

样例输入

in 复制代码
4
4 3 2 1

样例输出

out 复制代码
1 2 3 2

这段代码实现了一个优化的算法,用来解决题目中选手投票问题。我们来逐步分析其思路,并与解题过程对照。

c++代码

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

ll N, s, e, temp;
ll S[100006], prefix_sum[100006], answer[100006];

// 快速求区间和(返回值为ll,避免溢出)
ll get_interval_sum(int i, int j) {
    if (i <= 0 || j <= 0 || i > N || j > N || i > j) return 0;
    return prefix_sum[j] - prefix_sum[i - 1];
}

// 快速区间加一(差分思想)
void quick_add_one(int i, int j) {
    if (i <= 0 || j <= 0 || i > N || j > N || i > j) return;
    answer[i]++;
    if (j + 1 <= N) answer[j + 1]--;
}

int main() {
    cin >> N;
    fill(prefix_sum + 1, prefix_sum + N + 1, 0);
    fill(answer + 1, answer + N + 1, 0);

    for (int i = 1; i <= N; i++) {
        cin >> S[i];
        prefix_sum[i] = prefix_sum[i - 1] + S[i];
    }

    for (int i = 1; i <= N; i++) {
        s = 1, e = i - 1, temp = -1;
        while (s <= e) {
            int mid = (s + e) / 2;
            if (get_interval_sum(mid + 1, i - 1) <= S[i]) {
                temp = mid;
                e = mid - 1;
            }
            else s = mid + 1;
        }
        if (temp != -1) quick_add_one(temp, i - 1);

        s = i + 1, e = N, temp = -1;
        while (s <= e) {
            int mid = (s + e) / 2;
            if (get_interval_sum(i + 1, mid - 1) <= S[i]) {
                temp = mid;
                s = mid + 1;
            }
            else e = mid - 1;
        }
        if (temp != -1) quick_add_one(i + 1, temp);
    }

    // 计算差分前缀和,得到最终结果
    for (int i = 1; i <= N; i++) {
        answer[i] += answer[i - 1];
        cout << answer[i] << (i == N ? "\n" : " ");
    }
    return 0;
}//by wqs

思路和步骤分析

1. 前缀和计算

在这个算法中,首先我们使用前缀和数组来高效计算选手之间的影响力值总和。这样对于任意区间 [i, j],我们都可以在 O(1) 的时间内通过 prefix_sum[j] - prefix_sum[i-1] 来获得该区间的影响力值和。

cpp 复制代码
ll get_interval_sum(int i, int j) {
    if (i <= 0 || j <= 0 || i > N || j > N || i > j) return 0;
    return prefix_sum[j] - prefix_sum[i - 1];
}

2. 快速区间更新(差分思想)

为了高效地记录每个选手的投票数,我们使用了差分数组 answer[] 来进行快速更新。对于每一个选手,假设我们找到了满足投票条件的区间,我们可以将该区间的起始位置 i 处加 1,并且在该区间的结束位置 j+1 处减 1。然后,通过累加差分数组,最终得到每个选手的投票数。

cpp 复制代码
void quick_add_one(int i, int j) {
    if (i <= 0 || j <= 0 || i > N || j > N || i > j) return;
    answer[i]++;
    if (j + 1 <= N) answer[j + 1]--;
}

3. 二分查找优化

接下来,我们使用二分查找来寻找每个选手 i 在左侧和右侧可以影响的范围。我们要找的条件是,选手 j 会投票给选手 i,当且仅当 S[j] >= sum(S[i+1]...S[j-1])。这里的 sum(S[i+1]...S[j-1]) 就是我们通过前缀和数组求得的区间和。

  • 左侧范围: 我们需要找到选手 i 左边的选手 j,使得从 ji-1 之间的总影响力不超过 S[i]。我们通过二分查找确定这个范围。
  • 右侧范围: 同样,我们需要找到选手 i 右边的选手 j,使得从 i+1j-1 之间的总影响力不超过 S[i]。我们也通过二分查找确定这个范围。
cpp 复制代码
s = 1, e = i - 1, temp = -1;
while (s <= e) {
    int mid = (s + e) / 2;
    if (get_interval_sum(mid + 1, i - 1) <= S[i]) {
        temp = mid;
        e = mid - 1;
    }
    else s = mid + 1;
}
if (temp != -1) quick_add_one(temp, i - 1);

s = i + 1, e = N, temp = -1;
while (s <= e) {
    int mid = (s + e) / 2;
    if (get_interval_sum(i + 1, mid - 1) <= S[i]) {
        temp = mid;
        s = mid + 1;
    }
    else e = mid - 1;
}
if (temp != -1) quick_add_one(i + 1, temp);

4. 最终结果计算

通过上述步骤,所有满足条件的投票已经通过差分数组 answer[] 记录下来。最后,我们只需计算前缀和,得到每个选手的投票数。

cpp 复制代码
for (int i = 1; i <= N; i++) {
    answer[i] += answer[i - 1];
    cout << answer[i] << (i == N ? "\n" : " ");
}

5. 整体流程

  1. 读入数据并初始化前缀和和差分数组。
  2. 对每个选手,使用二分查找计算其能影响的区间并更新差分数组。
  3. 使用前缀和计算出每个选手的最终投票数并输出结果。

时间复杂度分析

  • 前缀和的计算: O(N),直接遍历一次数组。
  • 二分查找: 对于每个选手,我们分别进行两次二分查找,每次查找的时间复杂度为 O(log N)。所以,总的时间复杂度是 O(N log N)
  • 差分数组更新和最终求和: 通过差分数组,我们在 O(N) 时间内完成投票数的更新和计算。

所以,整体时间复杂度为 O(N log N),适合在 N 较大的情况下使用。

总结

这段代码巧妙地结合了 前缀和差分数组 技术,并使用 二分查找 来优化区间和的计算。这样既能保证算法的正确性,又能够高效地解决大规模数据的处理问题。

相关推荐
2401_872418787 小时前
算法入门:数据结构-堆
数据结构·算法
xwz小王子8 小时前
手术机器人登上Science Robotics:2毫米纤细手臂,从3厘米切口完成腰椎神经减压
算法·机器人
拼尽全力前进8 小时前
Guava Cache vs Caffeine 面试详解
面试·职场和发展·guava
黎阳之光9 小时前
视频孪生智护供水生命线:黎阳之光赋能医疗与园区水务高质量升级
运维·物联网·算法·安全·数字孪生
程序员小远9 小时前
Python自动化测试框架及工具详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
Black蜡笔小新9 小时前
自动化AI算法训练服务器DLTM制造业AI质检工作站助力制造业实现AI智检
人工智能·算法·自动化
嵌入式小能手10 小时前
飞凌嵌入式ElfBoard-进程间的通信之命名管道
linux·服务器·算法
啦哈拉哈10 小时前
Leetcode题解记录-hot100(81-100)
算法·leetcode·职场和发展
csdn_aspnet10 小时前
Java 霍尔分区算法(Hoare‘s Partition Algorithm)
java·开发语言·算法
诸葛务农10 小时前
道路行驶条件下电动汽车永磁电机的有效使用寿命及永磁体的失效和回收再利用(下)
java·开发语言·算法