NOIP 2013 小朋友的数字

来源:互联网 发布:python 频域 编辑:程序博客网 时间:2024/04/27 14:24

【题目链接】点击打开链接

【解题思路】

           对于最大子段和问题,我们有O(N)的算法。 具体的做法是这样的:当前要求第I位及之前的最大子段和,如果第(I-1)位及之前的最大子段和大于0,则显然这一位取了也未尝不可(不会减少),也就是当前这一位和前面一段连接起来。否则的话,就新开一段——把前面的最大子段和改成0以后继续往下扫描。 如果一定要说这是DP也可以。
这样朴素的做能得50分, 在计算特征值与分数的过程中记录一下最大值可以的得到80分, 原因在与最后两个点的分数值超过了longlong。
进一步分析可以发现除了第一个小朋友外剩下的小朋友的分数值是不下降的。所以对于一个小朋友他的分数只有两种情况。
1.如果他的前一个小朋友的特征值大于0,那他的分数就为前一个小朋友的分数加上前一个小朋友的特征值。更新当前最大值。
2.如果他的前一个小朋友的特征值小于0,那他的分数就为第二个小朋友的分数。
当一个小朋友的分数值大于1000000000时我们取模
因为第一个小朋友的分数不会大于1000000000,所以我们就可以在计算过程中判断出来是否有小朋友的分数大于第一个小朋友。
这样就可以拿到满分。。

【AC代码】

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define ll long longconst int maxn=1000010;ll n,p,maxsum;ll a[maxn];ll sum[maxn],t[maxn];//前缀和和特征值ll b[maxn];//记录分数int main(){    scanf("%I64d%I64d",&n,&p);    for(int i=1; i<=n; i++) scanf("%lld",&a[i]);    sum[1]=a[1];    maxsum=a[1];    t[1]=a[1];    for(int i=2; i<=n; i++)//维护前缀和    {        sum[i] = max(sum[i-1]+a[i],a[i]);        t[i]   = max(sum[i],maxsum);        maxsum   = t[i];    }    b[1] = t[1];    b[2] = t[1]+b[1];    bool fuck=false;    if(b[2]>=b[1])    {        fuck=true;    }    for(int i=3; i<=n; i++)    {        if(t[i-1]>0)        {            b[i]=b[i-1]+t[i-1];            if(b[i]>b[1]) fuck=true;            if(b[i]>1e9)  b[i]%=p;        }        else            b[i]=b[2];    }    ll ans;    if(fuck)    {        ans = b[n];    }    else        ans = b[1];    ans=ans%p;    printf("%lld\n",ans);    return 0;}

【参考博客】点击打开链接

0 0
原创粉丝点击