[NOIP2013][vijos1850]小朋友的數字(dp+貪心)
題目描述
題解
感覺vijos的資料好強啊。在codevs上跑過了然而被卡常數。
其實這道題的題意是很好懂的,但是我發現了兩個坑點:
①算特徵值的時候的dp,f(i)表示以i結尾的最長連續子序列和,所以最終某個人的特徵值F(i)=f(j),1<=j<=i。這個錯誤非常不應該,以後應該注意。
②很多人想當然或者大概一算覺得答案應該不會超過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一定要避免犯不該犯的錯誤,想清楚狀態。
②資料範圍一定不要算錯了,不要想當然。