◇NOIP2013 普及组◇ 小朋友的数字


Description

有 n 个小朋友排成一列。每个小朋友手上都有一个数字,这个数字可正可负。规定每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)小朋友手上的数字之和的最大值。
作为这些小朋友的老师,你需要给每个小朋友一个分数,分数是这样规定的:第一个小朋友的分数是他的特征值,其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。
请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对 p 取模后输出。
Input
输入文件为 number .in。
第一行包含两个正整数 n、p,之间用一个空格隔开。
第二行包含 n 个数,每两个整数之间用一个空格隔开,表示每个小朋友手上的数字。
Output
输出文件名为 number .out。
输出只有一行,包含一个整数,表示最大分数对 p 取模的结果。
Sample Input
【样例1】
5 997
1 2 3 4 5
【样例2】
5 7
-1 -1 -1 -1 -1
Sample Output
【样例1】
21
【样例2】
-1

【输入输出样例说明】

1. 小朋友的特征值分别为 1、3、6、10、15,分数分别为 1、2、5、11、21,最大值 21对 997 的模是 21。
2. 小朋友的特征值分别为-1、-1、-1、-1、-1,分数分别为-1、-2、-2、-2、-2,最大值-1 对 7 的模为-1,输出-1。
对于 50%的数据,1 ≤ n ≤ 1,000,1 ≤ p ≤ 1,000所有数字的绝对值不超过 1000;
对于 100%的数据, 1 ≤ n ≤ 1,000,000, 1 ≤ p ≤ 10^9, 其他数字的绝对值均不超过 10^9

 

题目解析
由题目描述可知,本题需要进行2次求值操作——第一次是求特征值,第二次是求分数。特征值要求 连续 若干个小朋友,即令该小朋友的编号为i,则要求找到[0,i]的一个子区间[p,q],使得该子区间中的值之和最大。那么求特征值就变成了一个连续字段和问题(可以直接套模板)。我们把字段和存入一个数组(sum)里……什么数组?答案是 long long ,int 会“炸”掉。那么
sum[i]=max(sum[i-1]+num[i],num[i])
(num[i]是第i个小朋友手上的数字)
意思很简单:取 第 i-1 个小朋友的特征值加上第 i 个小朋友手上的数 和 只取第 i 个小朋友手上的数字(什么时候这种情况会大一些?即 sum[i-1] 为负数) 的最大值。
再考虑特征值——其实就是区间 [1,i] 中的最大子段和,用C++表示就是 “algorithm”中的 max_element() 。但是 max_element() 的时间复杂度较高,我们可以用一个 Max 变量,存当前最大的特征值。同样,我们把分数存在 long long 数组(special)里。我们还可以做一个优化——由于 special[i] 只与 sum[1~i] 存在关联,我们可以把2个 for 循环融为一个,即先算出 sum[i] ,再算出 special[i]。
最后要求出分数,这其实是最容易的,只是不容易理解题意。借助一个非常简单的小学算数知识——一个数加上一个正数变大,加上一个负数变小。那么我们要找到 special 中的一个子序列,使它的总和最大,就可以只加上 special 数组 里面的正数,且由于最后1个小朋友的特征值是它们的总和,就不能加上去。但是有2种特殊情况——special 里面没有正数 或 只有1个小朋友…那么最大的应该是第一个小朋友的特征值(我也不知道为什么)。最后提醒——边做计算边取模!

 

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#define MAX 1000000
using namespace std;
int num[MAX+5];
long long sum[MAX+5],special[MAX+5],Mod,n;
int main()
{
    scanf("%d%d",&n,&Mod);
    for(int i=0;i<n;i++)
        scanf("%d",&num[i]);
    long long Max=num[0];
    sum[0]=special[0]=Max;
    for(int i=1;i<n;i++)
    {
        sum[i]=max(sum[i-1]+num[i],(long long)num[i]); //子段和
        special[i]=Max=max(Max,sum[i]); //特征值
    }
    long long ans=(special[0]*2)%Mod;
    for(int i=1;i<n-1;i++)
        if(special[i]>0) //避免减小 
            ans+=special[i],ans%=Mod;
    if(n==1 || (special[0]<0 && special[0]>ans))//特殊处理
        printf("%lld\n",special[0]%Mod);
    else
        printf("%lld\n",ans%Mod); 
    return 0;
}

The End

Thanks for reading!

-Lucky_Glass

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
noip2013普及初赛是全国信息学奥林匹克联赛的一场选拔赛。该比赛旨在选拔初学者,对编程和算法有一定基础的学生,通过比赛形式来考察他们的知识水平和解题能力。 比赛题目通常会涉及各个领域的算法数据结构,如图论、动态规划、数论等。题目难度逐步增加,从简单的输出结果,到复杂的程序设计与代码实现,考察选手的逻辑思维和编程能力。 参赛选手需要通过自己的思考和编程实现来解决题目,同时时间也是一个重要因素。比赛中,选手需要在规定的时间内独立完成所有题目,对于复杂的题目需要迅速想出解题思路并进行编码。因此,在比赛中,选手的临场发挥和解题速度也是需要考虑的因素。 noip2013普及初赛的结果将作为选拔阶段的一个重要依据,选出表现出色的选手进入到更高阶段的比赛,对于他们来说,这是一次展示自己实力的机会。 此外,noip2013普及初赛,也给了参赛选手一个交流的平台。选手们可以通过比赛结交同好,相互切磋,共同进步。同时,比赛结束后,还有详细的解题分析和讲解,有助于参赛选手对自己在比赛中的不足进行反思与改进。 总之,noip2013普及初赛是一个考察学生编程和算法能力的选拔赛,通过比赛的形式来选拔出优秀的选手。这对于参赛选手来说,是一次展示自己才华的机会,也是一个展示自己实力和提高自己能力的平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值