题解 P1982 【小朋友的数字】

题目链接

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('\n');//输出
    return 0;
}

这份代码运行时间还可以进一步缩短,\(fread\)读入优化蒟蒻懒得写了,各位有兴趣可以冲一下最优解

转载于:https://www.cnblogs.com/colazcy/p/11514760.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值