[dp记录]ARC117E Zero-Sum Ranges 2

\(\texttt{link}\)

贴个官方题解的图:

对于一个序列,对其做前缀和,则对数为 \(\sum\limits_{i=-n}^n \dbinom {cnt(i)} 2\)

从下往上一层一层决策,记 \(dp(i,j,c)\) 为占用了 \(i\) 个位置,分成了 \(j\) 段,对数为 \(c\) 的方案数,枚举上一层放 \(x\) 个数,则有转移:

\[\dbinom{x - 1}{j}dp(i,j,c) \longrightarrow dp(i+x,x-j,c+\binom x 2) \]

手玩一下便可知道段数为 \(x-j\)

考虑怎么计算答案,由于 \(1,-1\) 的数量是相等的,我们可以确定 \(s_0 = s_{2n} = 0\),也就是说,最终的前缀和 \(s\) 的图像大概长这样:

枚举 \(\ge 0\) 的段数 \(j\) 即可确定 \(\le 0\) 的段数必须为 \(j - 1\) 段(将 \(j\) 段连接在一起)。

于是有:

\[ans = \sum\limits_{i=2}^{2*n+1}\sum\limits_{j=1}^{n+1}\sum\limits_{c=0}^{k}dp(i,j,c)dp(2*n+1-i,j-1,k-c) \]

时间复杂度 \(\mathrm{O(n^5)}\)

\(\texttt{Code:}\)

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

inline void read(int &x) {
    x = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    x = f ? -x : x;
}

const int cmd = 998244353;
const int N = 35;

int fpow(int a, int b) {
    int res = 1;
    for (; b; b >>= 1, a = 1ll * a * a % cmd)
        if (b & 1) res = 1ll * res * a % cmd;
    return res;
}

int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}

int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}

int n, k;

ll dp[N << 1][N][N * N], binom[N << 1][N << 1];

int main() {
    // freopen("a.in", "r", stdin);
    // freopen("a.out", "w", stdout);
    read(n); read(k);
	for (int i = 0; i <= 2 * n; i++) {
		binom[i][0] = 1;
		for (int j = 1; j <= i; j++)
			binom[i][j] = binom[i - 1][j] + binom[i - 1][j - 1];
	}
    for (int i = 0; i <= n + 1; i++)
    if (i * (i - 1) / 2 <= k) dp[i][i][i * (i - 1) / 2] = 1;
	for (int i = 0; i <= 2 * n + 1; i++)
	for (int j = 1; j <= min(i, n + 1); j++)
	for (int c = 0; c <= k; c++)
	if (dp[i][j][c]) {
		for (int x = j + 1; x <= n * 2 + 1 - i; x++)
		if (c + x * (x - 1) / 2 <= k && x - j <= n + 1)
			dp[i + x][x - j][c + x * (x - 1) / 2] += dp[i][j][c] * binom[x - 1][j];
	}
	ll ans = 0;
	for (int i = 2; i <= 2 * n + 1; i++)
	for (int j = 1; j <= n + 1; j++)
	for (int c = 0; c <= k; c++)
		ans += dp[i][j][c] * dp[n * 2 + 1 - i][j - 1][k - c];
	printf("%lld", ans);
    return 0;
}
posted @ 2021-11-12 15:55  klii  阅读(97)  评论(0)    收藏  举报