P1982 [NOIP2013 普及组] 小朋友的数字
pure__Elysia · · 题解
本blog包含:
告别高精__int128
啊?我当年居然没抄题解吗???
前言写得又臭又长写给谁看啊
题目链接
通过记录
前言:
太离谱了!!!2022.10.9集训期间考试考了这道题,我读错了题,爆零了。然后考完重新读题,感觉做不起了,机房好多大佬也在讨论是写__int128好还是高精好。然后我打算看洛谷题解。结果:
WTM在2018年就AC了这道题,还是一遍过!!!
这不对吧,4年前我才刚刚学OI啊。虽然说是普及组的题,也不可能一遍过吧。
然后我就去翻了洛谷现存的16篇题解,没有一个一样,甚至没有一个相像!
我当场去搜索CSDN,WOW,居然也没有像的。
然后我就开始把当年的码改成我现在的码风。结果惊讶的发现:
1.当年不会快读甚至不会scanf,于是写的cin,cout(是的,过得了洛谷过不了加强数据)。 2.不会define,全篇long long老老实实写的。
3.一个用来判断、标记变量名直接取名pd,以为判断(这个习惯现在都还没改,经常命名为bj(标记),pd(判断),hf(合法))。
4.当年不熟悉bool(真的是刚学OI),上面说的那个pd都是用的int存的。
结论:我没抄题解,而且刚学OI的2018年的我薄纱现在的我!!!
我加了快读 register O2,手写max,甚至最优解挤进了第一页!!
去重第15合影留念
WTF
题目描述
有
然后每个小朋友有一个分数,第一个小朋友的分数就是给定数也是特殊值。之后每个小朋友的分数是在他之前(不含自己)的所有小朋友中,那些小朋友特殊值+分数最大的那个,就是他的分数了。
WTF好绕好绕
算法思路
很明显,本题分为两个部分:1.计算特殊值。2.计算分数。
先说简单的。当我们记录完特殊值之后,求分数直接走一个记一个最大值就行了。一个分数最大,一个“分数+特殊值”最大。
那么怎么求特殊值。首先暴力好像,就是挨个算。但明显这个就是个前缀维护就好,很容易想到。
对啊,就一个前缀,去维护这个“最大给定数和”,连着的正数就加进去,完了记个maxn方便记录特殊值,解决了啊。有什么难的???
特殊处理
难点在这里:
在普及组时,最经典的就是被卡int。于是也有“十年OI一场空,不开long long见祖宗”的说法。现在我们机房基本上都会写个define 一个long long。
但这题就喜剧了,他不仅卡int,long long他还刚好存不下!!
选择1:涉及到ans,和的,选择用__int128来存储。
选择2:你了不起,你清高,你普及组写高精。
选择3:一个一个记,从上一个转移,既然是一个一个转移(贪心捏),那我就边存边模嘿嘿。取模之后的判断大小正确性?反正数据范围说单个数都是小于 (我当年是怎么整出这么骚的想法的啊)
代码实现
这篇2018年的漏洞百出的代码,我自己都差点没看懂
#pragma GCC optimize(2)//抢别人家评测机最优解用的O2
#include<bits/stdc++.h>
using namespace std;
#define int long long//祖宗:嗨害嗨
#define ri register int//大爷来卡常啊
int a[1000001][4];//分别存储:特殊值,给定数,贪的分数
//我也不知道我当年为什么非要开成二维数组,开四个一维不好吗?
inline int rd()//过强化数据必备
{
int a=0,f=1;
char c=getchar();
for(; c<'0'||c>'9'; c=getchar()) if(c=='-') f=-1;
for(; c>='0'&&c<='9'; c=getchar()) a=(a<<1)+(a<<3)+c-'0';
return a*f;
}
inline int Max(int a,int b){if(a>b)return a;else return b;}
//卡常一下又何妨
signed main()
{
int n,p,maxn;
n=rd(),p=rd();
int x;
x=rd();
a[1][1]=x;
a[1][0]=x;
maxn=x;
for(ri i=2;i<=n;i++) //计算特殊值
{
x=rd();
if(a[i-1][1]>0)
a[i][1]=a[i-1][1]+x;//之前是正数就加了
else
a[i][1]=x;//负数?那就从现在开始
a[i][0]=Max(maxn,a[i][1]);//维护前缀最大和
maxn=a[i][0];
}
int pd=0;//正确的,这里应该开bool
a[1][2]=a[1][0];
a[2][2]=2*a[1][0];
if(a[2][2]>=a[1][2])
pd=1;//毕竟从下面是第三位开始,那这里判一下第二位
for(ri i=3;i<=n;i++)
{
if(a[i-1][0]<0)
a[i][2]=a[i-1][2];//正数就给我贪过来
else
{
a[i][2]=a[i-1][2]+a[i-1][0];//特殊值加上来的干活
if(a[i][2]>a[1][2])
pd=1;
if(a[i][2]>1000000000)//究极防爆盾
a[i][2]%=p;
}
}
if(pd==1)
printf("%lld\n",a[n][2]%p);//上面可不一定取了模的,最后确保一下
else
printf("%lld\n",a[1][2]%p);
return 0;
}