【CSP-S 2019】D2T2 划分

Description

传送门

Solution

算法1 12pts

指数算法随便乱搞。

算法2 36pts

\(O(n^3)\)dp。

\(f_{i,j}\)表示以位置\(j\)结尾,上一个决策点为\(j\)时的最小值。

转移也是显而易见的:

\(s_i=\sum \limits_{j=1}^{i} a_j\),即前缀和。

\[f_{i,j}=f_{j,k}+(s_i-s_j)^2,其中 s_i-s_j \ge s_j-s_k \]

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

inline ll ty() {
	char ch = getchar(); ll x = 0, f = 1;
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

const int _ = 5000 + 10;
int n, type;
ll A[_], sum[_], f[_][_], ans;

inline ll _2(ll x) { return x * x; }

int main() {
	n = ty(), type = ty();
	for (int i = 1; i <= n; ++i) {
		A[i] = ty();
		sum[i] = sum[i - 1] + A[i];
		// cout << sum[i] << endl;
	}
	memset(f, 0x3f, sizeof(f));
	ans = f[0][0];
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= i; ++j) {
			if (j == 1) f[i][j] = _2(sum[i]);
			else
				for (int k = 1; k < j; ++k)
					if (sum[i] - sum[j - 1] >= sum[j - 1] - sum[k - 1])
						f[i][j] = min(f[i][j], f[j - 1][k] + _2(sum[i] - sum[j - 1]));
		}
	}
	
	for (int i = 1; i <= n; ++i) ans = min(ans, f[n][i]);
	printf("%lld\n", ans);
	return 0;
}

算法3 64pts

\(O(n^2)\)dp。这个因为我太蒻了,所以不会写。大概思路就是

“可以固定\(j\),发现在移动\(i\)的过程中,\(k\)也在移动,满足一个单调性,然后我们维护一个\(f_{j,k}\)的最小值就可以了”

算法4 84/100pts

据说通过打表可以发现证明链接,当最后一段在满足有解的情况下和最小时,答案最优。

因此可以设\(g_i\)表示到位置\(i\)时,在有解的情况下值最小的上一个决策点的位置,即

\[g_i = \max \limits_{j=0}^{i-1}j, s.t. s_i-s_j \ge s_j-s_{g_j} \]

稍微移个项就可以得到

\[s_i \ge 2s_j-s_{g_j} \]

显然这是单调的,于是用一个单调队列维护即可。

Code

#include <bits/stdc++.h>
using namespace std;

inline int ty() {
	char ch = getchar(); int x = 0, f = 1;
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

typedef long long ll;
const int _ = 5e5 + 10;
int n, type, a[_], q[_], g[_];
ll sum[_], ans;

inline ll calc(int j) { return 2ll * sum[j] - sum[g[j]]; }
inline ll _2(const ll &x) { return x * x; }

int main() {
#ifndef ONLINE_JUDGE
	freopen("divide.in", "r", stdin);
	freopen("divide.out", "w", stdout);
#endif
	n = ty(), type = ty();
	for (int i = 1; i <= n; ++i) a[i] = ty(), sum[i] = sum[i - 1] + a[i];
	int h = 1, t = 1;
	for (int i = 1; i <= n; ++i) {
		while (h < t && calc(q[h + 1]) <= sum[i]) ++h;
		g[i] = q[h];
		while (h < t && calc(q[t]) >= calc(i)) --t;
		q[++t] = i;
	}
	int now = n;
	while (now) {
		ans = ans + _2(sum[now] - sum[g[now]]);
		now = g[now];
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2019-12-12 20:43  newbielyx  阅读(487)  评论(0编辑  收藏  举报