The Contest(思维+前缀和)

本文详细解析了 Codeforces 平台上的题目 1257E The Contest 的解题思路。通过巧妙运用前缀和与后缀最小值的概念,文章提出了一种高效的算法来解决三个参与者之间的数字分配问题,目标是最小化操作次数,以达到特定的数字排列。代码实现使用 C++ 完成。

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

题目链接:E. The Contest

题意:
三个人,每个人有一些数字,组合起来是一个排列,现在给你三个人手中数字的数量以及手中是哪几个数,每次操作可以把一个人手中的数字给另个人,问你最少多少次操作使得第一个人拥有这个排列的某个前缀,第三个人拥有这个排列的某个后缀,第二个人拥有中间剩下的部分(允许有人没有数字)。

思路:

  1. 看到三个人操作,我们先看两个人操作时的情况:
    假设到最后,第一个人拥有1 ~ i,第二个人拥有i+1 ~ n,那么最小操作数为第二个人1~i中拥有的数字加上第一个人i+1 ~ n中拥有的数字。我们可以采用前缀和,cnt1[k]表示第一个人前k个数中拥有的个数,cnt2[k]表示第二个人前k个数中拥有的个数,则表达式为:
    cnt2[i]+cnt1[n]−cnt1[i]
  2. 受到启发我们看三个人操作时的情况:
    假设到最后,第一个人拥有1 ~ i,第二个人拥有i+1 ~ j,第三个人拥有j+1 ~ n,那么最小操作数为第二个人和第三个人1 ~ i中拥有的个数加上第一个人和第三个人i+1 ~ j中拥有的个数加上第一个人和第二个人j+1 ~ n中拥有的个数。我们可以采用前缀和,cnt1[k]表示第一个人前k个数中拥有的个数,cnt2[k]表示第二个人前k个数中拥有的个数,cnt3[k]表示第三个人前k个数中拥有的个数字表达式为:
    cnt2[i]+cnt3[i]+cnt1[j]−cnt1[i]+cnt3[j]−cnt3[i]+cnt1[n]−cnt1[j]+cnt2[n]−cnt2[j]
  3. 化简得到:
    cnt2[i]−cnt1[i]+cnt3[j]−cnt2[j]+cnt1[n]+cnt2[n]
  4. 我们从0~n枚举i,接下来我们考虑j的取值,我们可以看到对于固定的i,只需要找到一个j使得该式子最小即可,那么我们可以设置一个后缀minn[]数组,minn[i]表示当i≤j≤n时,cnt3[j]−cnt2[j]最小的值,那么答案即为:
    cnt2[i]−cnt1[i]+minn[i]+cnt1[n]+cnt2[n]

参考博客:CODEFORCES 1257E - THE CONTEST

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;

const int N = 200010;

int n, m, k;
int num1[N];
int num2[N];
int num3[N];
int mini[N];
int a[N], b[N], c[N];

int main()
{
    scanf("%d %d %d", &n, &m, &k);
    int tot = n + m + k;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        num1[a[i]]++;
    }
    for(int i = 1; i <= m; i++)
    {
        scanf("%d", &b[i]);
        num2[b[i]]++;
    }
    for(int i = 1; i <= k; i++)
    {
        scanf("%d", &c[i]);
        num3[c[i]]++;
    }
    for(int i = 1; i <= tot; i++)
    {
        num1[i] += num1[i - 1];
        num2[i] += num2[i - 1];
        num3[i] += num3[i - 1];
    }
    mini[tot + 1] = inf;
    for(int i = tot; i >= 0; i--)
    {
        int now = num3[i] - num2[i];
        mini[i] = min(mini[i + 1], now);
    }
    int ans = inf;
    for(int i = 0; i <= tot; i++)
    {
        int now = num2[i] - num1[i] + num1[tot] + num2[tot] + mini[i];
        ans = min(ans, now);
    }
    printf("%d\n", ans);
    return 0;
}

引用\[1\]:这段引用是一段C++代码,用于处理一个01序列的查询。代码中使用了前缀和和后缀和的思想,通过计算每个位置之前1的个数和之后1的个数,来处理查询区间内的操作。具体的实现细节可以参考代码注释。\[1\] 引用\[2\]:在处理多组询问时,为了不改变非连续1的计数,代码将区间分成两段处理。左半段都是1的情况单独处理,后半段第一个位置为0,可以通过前缀和相减来计算。这样可以保证在处理查询时不会影响到非连续1的计数。\[2\] 引用\[3\]:这段引用是对一个二进制循环序列的定义和问题描述。一个好的二进制循环序列是指通过一系列操作可以使其变为空序列。而一个二进制循环序列的美丽度被定义为使其变为好序列所需的最小修改次数。每次修改可以翻转序列中的任意一个元素,即0变为1,1变为0。给定一个长度为n的二进制字符串a和q个查询,每个查询给出两个整数li和ri,需要回答f(aliria\[li..ri\]),其中我们将子字符串aliria\[li..ri\]视为一个循环序列。\[3\] 问题: 请问这段代码的作用是什么? 回答: 这段代码的作用是处理一个01序列的查询。通过计算每个位置之前1的个数和之后1的个数,来处理查询区间内的操作。在处理多组询问时,为了不改变非连续1的计数,代码将区间分成两段处理,保证在处理查询时不会影响到非连续1的计数。最终输出每个查询区间的结果。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* [ICPC22网络赛(1) - A 01 Sequence(前缀和思维)](https://blog.youkuaiyun.com/Mr_dimple/article/details/126983251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [The 2022 ICPC Asia Regionals Online Contest - A 01 Sequence](https://blog.youkuaiyun.com/qq_35339563/article/details/126959132)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值