状压
火箭连接大脑,暴力代替思考!
题目问的是一个序列的所有 题解区
后面这一段和其他题解相同。
定义
可以得出转移方程
其中初始化为
随后我们假设一个序列的贡献只产生再最长的最大前缀上。
我们的答案就是
我们开心的码完代码发现只有 55 分,为什么,重新观察刚才的思考过程,我们发现我们会将『所有非空前缀和都小于
为了处理这个,其他题解开始各显神通,但是其中一种神通似乎都没有说,这里我要说一下这种方法(这也是我写这篇题解的目的)。
题目要求找『最大非空前缀和之和』而我们求的是『最大前缀和之和』,我们简单思考就可以发现『最大非空前缀和之和』可以转化为『第一个元素加后面长为
时间复杂度是抽象的
#include <bits/stdc++.h>
#define int long long
#define MAXN 1200005
#define mod 998244353
using namespace std;
int n, A[MAXN], sum[MAXN], ans, f[MAXN], g[MAXN], cur_fir, a[1005][1005];
void solve(int *a) { // 直接调用获得 55pts 高分
memset(sum, 0, sizeof(sum));
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; i++) { // 枚举,算出 sum
for (int j = 0; j < (1 << n); j++) {
if (j & (1 << i - 1)) {
sum[j] += a[i];
}
}
}
f[0] = g[0] = 1;
for (int i = 1; i < (1 << n); i++) { // 按照上面的递推公式算出 f 和 g
for (int j = 1; j <= n; j++) {
if (i & (1 << j - 1)) {
if (sum[i] >= 0) {
f[i] += f[i ^ (1 << j - 1)];
f[i] %= mod;
}
if (sum[i] < 0) {
g[i] += g[i ^ (1 << j - 1)];
g[i] %= mod;
}
}
}
}
for (int i = 0; i < (1 << n); i++) { // 按照上面的计算答案公式算出答案,记得 sum 要加一个当前的第一个元素
ans += (sum[i] + cur_fir) % mod * f[i] % mod * g[(1 << n) - 1 ^ i] % mod;
ans %= mod;
}
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> A[i]; // 这个是原始的序列
}
n--; // 方便操作
for (int i = 1; i <= n + 1; i++) {
cur_fir = A[i];
int *b = a[i]; // 拷贝一下原始序列,A 删掉 A[i] 然后存进 b
for (int j = 1; j < i; j++) {
b[j] = A[j];
}
for (int j = i + 1; j <= n + 1; j++) {
b[j - 1] = A[j];
}
solve(b);
}
cout << (ans % mod + mod) % mod;
return 0;
}