题目描述
题解
感觉vijos的数据好强啊。在codevs上跑过了然而被卡常数。
其实这道题的题意是很好懂的,但是我发现了两个坑点:
①算特征值的时候的dp,f(i)表示以i结尾的最长连续子序列和,所以最终某个人的特征值F(i)=f(j),1<=j<=i。这个错误非常不应该,以后应该注意。
②很多人想当然或者大概一算觉得答案应该不会超过long long,但是实际上是完全有可能的。极端情况:假设有
106
个小朋友,每一个小朋友的数字都是
109
,那么他们的特征值就应该为
109,2∗109,3∗109……106∗109=1015.
而每个小朋友的分数就应该为
109,2∗109,4∗109……(1+(1+106)∗1092)∗109
,这很显然已经超过了longlong的范围。其实数据还是比较良心的,实际上不在极限情况下也是很容易爆long long的。
而且还有一个问题是,不能一边做一边取模。因为题目的要求是求出来最大的值然后再取模。如果直接取模的话原先大的值再模意义下有可能变成小的。
那该怎么做呢?
我们可以发现一个非常有用的性质:除了第一个小朋友,所有小朋友的特征值和分数都是不降的。
那么对于某一个小朋友,他的分数只有两种情况
①如果他前一个小朋友的特征值大于0,说明前面所有小朋友的特征值和分数都是一个不降的数列,那么这个小朋友的分数就为他前一个小朋友的分数加上特征值。
①如果他前一个小朋友的特征值小于0,说明前面所有小朋友的特征值是一个负数数列,分数是一个负常数列,那么这个小朋友的分数就为第二个小朋友的分数加上特征值。
然后特判一下第一个小朋友。
虽然这道题是普及组的,但是还是比较有趣的。worth a try.
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
#define N 1000005
const LL inf=1e18;
int n;
LL Mod,Max,a[N],f[N],g[N],h[N],ans;
bool flag;
int main()
{
scanf("%d%lld",&n,&Mod);
for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
f[1]=h[1]=a[1];Max=a[1];
for (int i=2;i<=n;++i)
{
h[i]=max(h[i-1]+a[i],a[i]);
f[i]=max(f[i-1],h[i]);
}
g[1]=f[1];g[2]=f[1]+g[1];
if (g[2]>=g[1]) flag=true;
for (int i=3;i<=n;++i)
{
if (f[i-1]>0)
{
g[i]=g[i-1]+f[i-1];
if (g[i]>=g[1]) flag=true;
if (g[i]>inf) g[i]%=Mod;
}
else g[i]=g[2];
}
if (!flag) ans=g[1]%Mod;
else ans=g[n]%Mod;
printf("%lld\n",ans%Mod);
}
总结
①dp一定要避免犯不该犯的错误,想清楚状态。
②数据范围一定不要算错了,不要想当然。