P1982小朋友的数字
手疼qwq
翻译一下题面。就是说,给n个数,第i个数(包括第i个)以及之前的数构成的最大子段和是i的特征值,i以前(不包括i)的数中最大的分数j+特征值j是i的分数,求所有人中的最大分数。
(好吧翻译之后更看不懂了qwq)
这看起来像一个dp,还是两次dp
那我们就拿dp试试(太过蒟蒻不会写暴力)
对于特征值,我也是看了题解才知道这是个最大子段和问题
那我们看一下最大字段和怎么做
1.暴力(O(n^3))显然超时,不讨论
2.分治算法(我不会)
3.dp(补习完滚回来写博) (O(n)(最快的一种算法))
所有算法请走这里
这里我们简单的说一下dp求最大子段和
设dp[i]是以num[i]结尾的最大子段和(这个子段中一定包含num[i])
则dp[i]=max(num[i],dp[i-1]+num[i])
相当于说要么是之前的dp[i-1]再加上num[i]是以num[i]结尾的最大子段和,要么之前的不要了,选a[i]作为dp[i]
最终答案当然不是dp[n],因为num[n]不一定要在最大子段结尾。
最终答案是在所有的dp[i]中的最大值
于是就有了求t[i](特征值)的代码了
long long maxn=-2147483647; for(int i=1;i<=n;i++) { dp1[i]=max(dp1[i-1]+num[i],num[i]); if(dp1[i]>maxn)maxn=dp1[i]; t[i]=maxn%p;//一个unbelievable的地方:中间不模会炸!!!(就算是小数据也会炸qwq)(未解之谜*1) }
接下来求分数
未解之谜2警告
我们设maxn为当前所有f[i](分数)中的最大值,那么maxn的转移方程:
maxn=max(maxn,f[i-1]+t[i-1])
f[i]=maxn
未解之谜2:
设f[i]为当前i的分数,则f[i]为之前的所有f[j](1<=j<i)中最大的一个,f[i]=max(f[i-1],f[i-1]+t[i-1])
但上面那个是不对的,这是为什么呢?我也不知道啊。
好了我们自动忽视上面的未解之谜2
如果maxn>ans,则ans=maxn%p(一定要在这里模,放在最后模会wa(未解之谜1))
所以我们就有了ac代码(%%%神仙ych)
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=1000005; long long n,p,num[N],dp1[N],f[N],t[N],ans=-2147483647; int main() { scanf("%lld%lld",&n,&p); for(int i=1;i<=n;i++) scanf("%lld",&num[i]); long long maxn=-2147483647; for(int i=1;i<=n;i++) { dp1[i]=max(dp1[i-1]+num[i],num[i]); if(dp1[i]>maxn)maxn=dp1[i]; t[i]=maxn%p; } maxn=-2147483647; f[1]=t[1];ans=f[1]; for(int i=2;i<=n;i++) { maxn=max(maxn,f[i-1]+t[i-1]); f[i]=maxn; if(maxn>ans)ans=maxn%p; printf("%d %lld\n",i,f[i]); } cout<<ans; }
综合两个未接之谜,数据可能有锅
我太菜了,找不到自己的锅
ps:如果能回答那两个未解之谜请评论,万谢qwq