|
|
|
|
## [$AcWing$ $455$. 小朋友的数字](https://www.acwing.com/problem/content/457/)
|
|
|
|
|
|
|
|
|
|
### 一、题目描述
|
|
|
|
|
有 $n$ 个小朋友排成一列。
|
|
|
|
|
|
|
|
|
|
**每个小朋友手上都有一个数字**,这个数字可正可负。
|
|
|
|
|
> **注**:$a[i]$
|
|
|
|
|
|
|
|
|
|
规定每个小朋友的 **特征值** 等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)小朋友手上的数字之和的最大值。
|
|
|
|
|
> **注:$b[i]$,特征值=最大子段和**
|
|
|
|
|
|
|
|
|
|
作为这些小朋友的老师,你需要给每个小朋友一个 **分数** ,分数是这样规定的:
|
|
|
|
|
> **注**:$c[i]$
|
|
|
|
|
|
|
|
|
|
第一个小朋友的分数是他的特征值
|
|
|
|
|
> **注**:$c[1]=b[1]$
|
|
|
|
|
|
|
|
|
|
其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。
|
|
|
|
|
|
|
|
|
|
请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对 $p$ 取模后输出。
|
|
|
|
|
|
|
|
|
|
**输入格式**
|
|
|
|
|
第一行包含两个正整数 $n、p$,之间用一个空格隔开。
|
|
|
|
|
|
|
|
|
|
第二行包含 $n$ 个数,每两个整数之间用一个空格隔开,表示每个小朋友手上的数字。
|
|
|
|
|
|
|
|
|
|
**输出格式**
|
|
|
|
|
输出只有一行,包含一个整数,表示最大分数对 $p$取模的结果。
|
|
|
|
|
|
|
|
|
|
**数据范围**
|
|
|
|
|
$1≤n≤10^6,1≤p≤10^9$,其他数字的绝对值均不超过$10^9$
|
|
|
|
|
|
|
|
|
|
**输入样例1** :
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
5 997
|
|
|
|
|
1 2 3 4 5
|
|
|
|
|
```
|
|
|
|
|
**输出样例 1**:
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
21
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**输入样例 2**:
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
5 7
|
|
|
|
|
-1 -1 -1 -1 -1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**输出样例 2**:
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
-1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 二、前置知识
|
|
|
|
|
**[$AcWing$ $126$. 最大的和](https://www.cnblogs.com/littlehb/p/17744300.html)**
|
|
|
|
|
|
|
|
|
|
https://www.acwing.com/solution/content/192382/
|
|
|
|
|
|
|
|
|
|
### 三、解题思路
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
typedef long long LL;
|
|
|
|
|
const int N = 1000010;
|
|
|
|
|
int n, p; // n个小朋友,模p
|
|
|
|
|
LL f[N]; // DP数组,前序最大子段和
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> p; // n个小朋友,模p
|
|
|
|
|
LL s = 0, mx = -1e18;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int x;
|
|
|
|
|
cin >> x;
|
|
|
|
|
s = max(s, 0ll) + x; // 最大子段和的套路,如果能继承父亲的财产就继承,如果父亲只有债务,就不管
|
|
|
|
|
mx = max(mx, s); // 到i为止可以获得的最大子段和
|
|
|
|
|
f[i] = mx; // 保存到dp数组f[N]中
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
① 每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个
|
|
|
|
|
(最少有一个)小朋友手上的数字之和的最大值。
|
|
|
|
|
② 第一个小朋友的分数是他的特征值,
|
|
|
|
|
其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。
|
|
|
|
|
*/
|
|
|
|
|
LL res = f[1]; // 第一个小朋友的得分目前是大王
|
|
|
|
|
|
|
|
|
|
LL score = f[1] * 2; // 第二个小朋友的得分 = 第一个小朋友的得分+第一个小朋友的特征值
|
|
|
|
|
|
|
|
|
|
for (int i = 2; i <= n; i++) { // 从第二个小朋友开始,到最后一个小朋友枚举
|
|
|
|
|
res = max(res, score); // res:记录最大分数
|
|
|
|
|
if (f[i] > 0) score += f[i]; // 如果最大子段和大于0,可以接在后面
|
|
|
|
|
}
|
|
|
|
|
printf("%lld\n", res % p); // 对p取模输出
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
上面的代码只能通过$10/12$,不能$AC$,因为会爆$long$ $long$,办法就是用$\_\_int128$
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
typedef __int128 LL;
|
|
|
|
|
const int N = 1000010;
|
|
|
|
|
int n, p;
|
|
|
|
|
LL f[N];
|
|
|
|
|
LL max(LL a, LL b) {
|
|
|
|
|
if (a >= b) return a;
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> p;
|
|
|
|
|
LL s = 0, mx = -1e36;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
int x;
|
|
|
|
|
cin >> x;
|
|
|
|
|
s = max(s, 0) + x;
|
|
|
|
|
mx = max(mx, s);
|
|
|
|
|
f[i] = mx;
|
|
|
|
|
}
|
|
|
|
|
LL res = f[1], score = f[1] * 2;
|
|
|
|
|
for (int i = 2; i <= n; i++) {
|
|
|
|
|
res = max(res, score);
|
|
|
|
|
if (f[i] > 0) score += f[i];
|
|
|
|
|
}
|
|
|
|
|
printf("%lld\n", res % p);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|