Solution [NOIP普及组2013] 小朋友的数字
题目大意:求(max{f[i]}),其中(f[i]= max{f[j]+ d[j] ;| ;j < i})d[j]$表示区间[1,j]内的最大连续子段和
动态规划
分析:看了题目题目大意,这题显然还是挺水的吧.
我们先看状态(d)怎么求:
我们设(td[i])表示以(i)结尾的最大连续子段和,那么显然
(td[i] = max(td[i - 1] + val[i],val[i])),(val[i])即第(i)个小朋友每人手上的数字
然鹅我们要求的是([1,j])内的最大连续子段和
所以(d[i] = max(d[i - 1],td[i]))这个显然还是很容易想到吧?
解决了状态(d)以后,我们再来看状态(f)如何求.这题的状态显然是(O(n))的,可是如果(O(n))转移的话,复杂度会炸成(O(n ^ 2)),无法接受.那么快速转移有几种方法:
- 数据结构,如线段树等(
我可能数据结构学傻了,线段树打到一半发现做复杂了),可以做到(O(logn))转移,总复杂度(O(nlogn)) - 临时变量 我们可以用一个临时变量(temp\_f)来表示(max{f[j] + d[j]; | ; 1 leq j < i}),然后直接(f[i] = temp\_f),(temp\_f = max(temp\_f,f[i] + d[i])),可以做到(O(1))转移,总复杂度(O(n))
然后我们发现,状态(td),(d),(f)之间互不依赖,我们完全可以一个(for)循环来进行状态的转移.并且第(i)个状态要么依赖第(i - 1)个状态,要么依赖区间([1,i - 1])内的极值,我们完全可以用临时变量将这些状态全部保存下来,然后程序就跑的飞快
另外此题有坑点,会爆(long;long)
解决方案:
- 手写高精度 但是这涉及到高精度取余,不好写
- 两个(long ; long)手动模拟(128)位整数 这个就是位运算版的高精了
- __int128 这个很方便,只是需要自己手写读入输出优化
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
typedef __int128 type;//我也是迫不得已
const type INF = 0x7fffffffffffffff;//就用long long的极值吧
template <typename T>
inline void read(T &x){//读入优化,注意有负数
x = 0;char c = getchar();int f = 1;
while(!isdigit(c))f = (c == '-') ? (-1) : f,c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
x *= f;
}
char buf[32];
template <typename T>
inline void write(T x){//输出优化
int p = 0;
do{
buf[++p] = x % 10;
x /= 10;
}while(x);
for(;p;p--)
putchar(buf[p] + '0');
}
type temp_d,d,temp_f,f,x,mod,ans = -INF;//几个临时变量,具体后面回收
int n;
int main(){
#ifdef LOCAL
freopen("fafa.in","r",stdin);//强烈建议使用宏编译,本机调试超级方便
#endif
read(n),read(mod);
read(x);//第一个小朋友很特殊
temp_d = d = x;//temp_d表示以i结尾的最大连续子段和,d表示区间[1,i]内的最大连续子段和
f = d;//由题意可知第一个小朋友的分数
temp_f = f + d;//temp_f表示区间[1,i - 1]内最大的f[j] + d[j]
ans = max(ans,f);//更新答案
for(int i = 2;i <= n;i++){
read(x);//读入
f = temp_f;//进行f的转移
ans = max(ans,temp_f);//更新答案
temp_d = max(temp_d + x,x);//求以i结尾的最大连续子段和
d = max(d,temp_d)//更新[1,i]内的最大连续子段和
temp_f = max(temp_f,f + d);//更新临时变量
}
if(ans < 0)putchar('-'),ans = -ans;//特判负数
write(ans % mod);putchar('
');//输出
return 0;
}
这份代码运行时间还可以进一步缩短,(fread)读入优化蒟蒻懒得写了,各位有兴趣可以冲一下最优解