给你一个长度为nn的序列aa,求它每一种排列的最大前缀和的和,对998244353取模

n20n\le 20

LOJ6433

Solution

不难发现,设最大前缀和的位置为pp,则在pp前面的那一段所有后缀和都>0>0pp后面的那一段所有前缀和都0\le 0

用状压dp分别计算这两种情况的方案数,记sum[i]sum[i]为状态ii包含所有aia_i的和

  1. 第一种情况考虑从后往前逐一加数,若状态ii满足sum[i]>0sum[i] > 0,那么可以往前添加一个不在ii中的数

  2. 第二种情况直接往后添加数,和1类似

答案就是isum[i]f[i]g[ALLi]\sum_{i} sum[i]*f[i]*g[ALL - i]

Summary

这道题并不难,但却想了挺久的时间

想题的时候抓不到题目的关键信息和重要性质,导致不知道从哪里下手,而且也想不太清楚

这还需要大量的做题、思考来弥补

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>

#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair<int, int> pii;

template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }

inline void proc_status()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) <<endl;
}

template <typename T> T read ()
{
T sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

const int Maxn = 20 + 10, Maxs = (1 << 20) + 100, Mod = 998244353;

int N, A[Maxn];
int f[Maxs], g[Maxs];
LL Sum[Maxs];

inline void Add (int &a, int b) { a += b; if (a >= Mod) a -= Mod; }

inline void Solve ()
{
int ALL = (1 << N) - 1;
for (int i = 0; i <= ALL; ++i) for (int j = 1; j <= N; ++j) if ((1 << (j - 1)) & i) Sum[i] += A[j];
for (int i = 1; i <= N; ++i) f[1 << (i - 1)] = 1;
g[0] = 1;
for (int i = 0; i <= ALL; ++i)
{
if (Sum[i] > 0)
{
for (int j = 1; j <= N; ++j) if (!((1 << (j - 1)) & i)) Add (f[i | (1 << (j - 1))], f[i]);
}
else
{
for (int j = 1; j <= N; ++j) if ((1 << (j - 1)) & i) Add (g[i], g[i ^ (1 << (j - 1))]);
}
}
int ans = 0;
for (int i = 0; i <= ALL; ++i)
Add (ans, 1ll * (Sum[i] + Mod) * f[i] % Mod * g[ALL ^ i] % Mod);
cout<<ans<<endl;
}

inline void Input ()
{
N = read<int>();
for (int i = 1; i <= N; ++i) A[i] = read<int>();
}

int main()
{
#ifdef hk_cnyali
freopen("A.in", "r", stdin);
freopen("A.out", "w", stdout);
#endif
Input();
Solve();
return 0;
}