首页 > 其他分享 >题解:CF2043C Sums on Segments

题解:CF2043C Sums on Segments

时间:2025-01-07 11:55:38浏览次数:1  
标签:右边 val min int 题解 Sums 左边 区间 CF2043C

题意

给你一个长度为 \(n\) 的数组 \(a\),满足 \(a\) 中有且仅有一个不为 \(1\) 也不为 \(-1\) 的数(以下简称特殊的值),剩余的数都是 \(1\) 或 \(-1\)。求所有可能的子区间的和的值(下文简称答案)。从小到大一次输出每一个值,每个值只输出一遍。

题解

首先,我们发现,如果把那个特殊的值考虑进来不那么好搞,而又“有且仅有一个不为 \(1\) 也不为 \(-1\) 的数”,于是我们考虑把 \(a\) 数组从那个特殊的值那里分成两段,他左边的成一段,他右边的另成一段,他自己提出来。然后我们就从考虑一个大问题分成了三个子问题:左边区间的所有可能的子区间的和的值,右边区间所有可能的子区间的和的值,包含特殊的值的所有可能的子区间的和的值。

左边区间 & 右边区间

这里有个不怎么显然的结论:在一个值域为 \(\{1,-1\}\) 的数组里,设他的最大子段和是 \(a\),最小子段和是 \(b\)。那么,有且仅有属于 \([b,a] \cap \mathbb{Z}\) 的数是答案,且这个集合里的每一个数都是答案。有了这个结论,左边区间的答案和右边区间的答案都好求了,但是,结论固然得证,所以我们就来证这个结论。

我们找到能造成最大子段和 \(a\) 的区间,然后,将它分成 \(a\) 个和为 \(1\) 的子区间,能够证明一定有分法。然后,如果我们想得到和为 \(a - 1\) 的子区间,我们只需要从左边或者右边去掉一个和为 \(1\) 的子区间就能得到。对于 \(a - 2,a - 3, \cdots 1\) 都可以用这种方法得出。于是我们就证得属于 \([1,a] \cap \mathbb{Z}\) 的数一定是答案。如法炮制,我们也能证出属于 \([b,-1] \cap \mathbb{Z}\) 也一定是答案。最后并上一个 \({0}\),就得到了 \([b,a] \cap \mathbb{Z}\)。这样我们就成功的求出了左边区间和右边区间的答案。

包含特殊值的区间

也是如法炮制,不过因为要包含特殊值,所以就不能简单的求最大(小)字段和,要求最大(小)前(后)缀和。对于左边区间就是后缀,对于右边,就是前缀。然后我们设左边的最大后缀和是 \(max_{1}\),最小后缀和是 \(min_{1}\)。右边的最大前缀和是 \(max_{2}\),最小前缀和是 \(min_{2}\),那么包含特殊值的区间的答案区间就是 \([min_{1} + min_{2} + val,max_{1} + max_{2} + val] \cap \mathbb{Z}\)。这里 \(val\) 指特殊值。

code

#include<bits/extc++.h>
#define int long long
using namespace std;
const int maxn = 2e5 + 5;
int n,val,pos,sum;
int max1,min1,max2,min2;
int a[maxn],dp1[maxn],dp2[maxn];
void solve()
{
    cin >> n;
    pos = val = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] != 1 && a[i] != -1)
        {
            val = a[i];
            pos = i;
        }
    }
    max1 = min1 = 0;
    fill(dp1 + 1,dp1 + n + 1,0);
    fill(dp2 + 1,dp2 + n + 1,0);
    for (int i = 1; i <= pos - 1; i++)   
    {
        dp1[i] = max(dp1[i - 1] + a[i],0ll);
        max1 = max(max1,dp1[i]);//求左边区间的最大子段和
        dp2[i] = min(dp2[i - 1] + a[i],0ll);
        min1 = min(min1,dp2[i]);//求左边区间的最小子段和
    }
    max2 = min2 = 0;
    fill(dp1 + 1,dp1 + n + 1,0);
    fill(dp2 + 1,dp2 + n + 1,0);
    for (int i = pos + 1; i <= n; i++)
    {
        dp1[i] = max(dp1[i - 1] + a[i],0ll);
        max2 = max(max2,dp1[i]);//求右边区间的最大
        dp2[i] = min(dp2[i - 1] + a[i],0ll);
        min2 = min(min2,dp2[i]);//求右边区间的最小
    }
    set<int>ans;
    for (int i = min(min1,min2); i <= max(max1,max2); i++)
        ans.insert(i);//先把不包含val的值加入
    max1 = min1 = sum = 0;
    for (int i = pos - 1; i >= 1; i--)
    {
        sum += a[i];
        max1 = max(max1,sum);//求左边区间的最大后缀和
        min1 = min(min1,sum);//求左边区间的最小后缀和
    }
    max2 = min2 = sum = 0;
    for (int i = pos + 1; i <= n; i++)
    {
        sum += a[i];
        max2 = max(max2,sum);//如法炮制
        min2 = min(min2,sum);
    }
    for (int i = (min1 + min2); i <= (max1 + max2); i++)
        ans.insert(val + i);//记录答案
    cout << ans.size() << endl;
    for (auto i : ans)
        cout << i << " ";
    cout << endl;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

upd:更新几处笔误

标签:右边,val,min,int,题解,Sums,左边,区间,CF2043C
From: https://www.cnblogs.com/Eous/p/18657371

相关文章

  • 题解:CF2057B Gorilla and the Exam
    CF2057BGorillaandtheExam思路不难发现其实每次操作就是把数组\(a\)内所有值为\(y\)的数都删除掉(\(y\)为数组\(a\)中的莫一个值)。所以我们需要把尽可能多的数都变成原来数组里出现次数最多的数(从出现数量最少的开始,这样能使得消失的数值种类最大化)。首先想到使用数组......
  • 题解:P11507 [ROIR 2017 Day 1] 计算器
    P11507[ROIR2017Day1]计算器思路简单的动态规划。\(dp_{i,j,k}\)表示使用了\(i\)次按钮A,\(j\)次按钮B和\(k\)次按钮C。转移式:\[\begin{cases}dp_{i+1,j,k}=\min(dp_{i+1,j,k},\lfloordp_{i,j,k}\div2\rfloor);\\dp_{i,j+1,k}=\min(dp_{i,j+1,k},\lfloo......
  • QOJ964. Excluded Min 题解
    QOJ原题链接简要题意设\(S\)为一个可重非负整数集合,假设\(x\)为\(S\)中的一个出现次数\(\ge2\)的元素,你可以将\(x\)改成\(x+1\)或\(x-1\)。定义\(f(S)\)表示对\(S\)进行上述操作任意次所能达到的最大\(\operatorname{mex}\)。给定一个长度为\(n\)的......
  • [POJ3237] 树的维护 题解
    一眼树链剖分或\(LCT\),由于在学后者所以就写了。取反操作相当于把\(min,max\)取反后交换,所以要维护\(min,max,val\)。时间复杂度\(O(m\logn)\)。#include<bits/stdc++.h>#definefa(x)lct[x].fa#definefl(x)lct[x].fl#definemx(x)lct[x].mx#definemn(x)lct[x]......
  • 复旦大学2024--2025学年第一学期(24级)高等代数I期末考试第七大题解答
    七、(10分) 设$V$是数域$\mathbb{K}$上的$n$维线性空间,$\varphi,\psi$是$V$上的幂等线性变换, 满足$\varphi\psi=\psi$且$\mathrm{Ker}\varphi$是$\psi$-不变子空间.证明:(1)$\mathrm{r}(\psi)\leq\mathrm{r}(\varphi)$;(2)若$\mathrm{r}(\psi)=\mathrm{......
  • 复旦大学2024--2025学年第一学期(24级)高等代数I期末考试第八大题解答
    八、(10分) 设$A,B$为$n$阶实矩阵,满足$A^2+B^2=AB$且$AB-BA$为非异阵, 求证:$n$是3的倍数且$|BA-AB|>0$.证明 设$\omega=-\dfrac{1}{2}+\dfrac{\sqrt{3}}{2}\mathrm{i}$,则$\omega^2=\overline{\omega}=-\dfrac{1}{2}-\dfrac{\sqrt{3}}{2}\mathrm{i}$,于......
  • Docker多阶段构建详解及问题解决
    在Docker的构建过程中,多阶段构建是一种非常强大的功能,它允许我们在一个Dockerfile中使用多个阶段来构建镜像,从而大大优化最终镜像的大小和构建过程。本文将详细介绍Docker多阶段构建的基本用法,并针对在使用该功能时可能遇到的问题提供解决方案。Docker多阶段构建基础多阶......
  • 题解:CF2057D Gifts Order
    传送门Statement单点修改,全局查询所有\([l,r]\)中区间极差减去区间长度的最大值,多组数据。Solution首先,如果区间的最大/最小值出现在区间中间,区间长度的改变并不会对其造成影响,反之,当最大值出现在区间左右端点时,区间长度改变可能会产生影响。在保证区间最大/最小值存在......
  • 题解:CF2057B Gorilla and the Exam
    传送门Statement给定数组\(a\),定义每次操作为选择区间\([l,r]\),记\(x=\min_{l\leqi\leqr}{a_i}\),删除区间内所有\(a_i=x\),给你\(k\)次修改的机会,每次修改某一个位置的数,问最少需要几次操作使得原数组全为\(0\)。Solution最初不做任何修改的操作次数定为原数组中......
  • Linux服务器无Root权限安装Cuda方法及问题解决
    CUDA简介什么是CUDA?CUDA(ComputeUnifiedDeviceArchitecture)是由NVIDIA提供的一种并行计算平台和编程模型,用于加速计算密集型任务。CUDA允许开发者使用GPU的计算能力,通过并行处理来快速执行复杂的计算任务。CUDA包括以下主要组成部分:CUDAToolkit:为开发人员提供工......