给你一个长度为n n n 的序列a a a ,求它每一种排列的最大前缀和的和,对998244353取模
n ≤ 2 0 n\le 20 n ≤ 2 0
Links LOJ6433
Solution 不难发现,设最大前缀和的位置为p p p ,则在p p p 前面的那一段所有后缀和都> 0 >0 > 0 ,p p p 后面的那一段所有前缀和都≤ 0 \le 0 ≤ 0
用状压dp分别计算这两种情况的方案数,记s u m [ i ] sum[i] s u m [ i ] 为状态i i i 包含所有a i a_i a i 的和
第一种情况考虑从后往前逐一加数,若状态i i i 满足s u m [ i ] > 0 sum[i] > 0 s u m [ i ] > 0 ,那么可以往前添加一个不在i i i 中的数
第二种情况直接往后添加数,和1类似
答案就是∑ i s u m [ i ] ∗ f [ i ] ∗ g [ A L L − i ] \sum_{i} sum[i]*f[i]*g[ALL - i] ∑ i s u m [ i ] ∗ f [ i ] ∗ g [ A L L − 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, 1l l * (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 ; }