bzoj 1044 [HAOI2008]木棍分割 二分答案+dp

题面

题目传送门

解法

第一问直接二分答案,第二问简单dp

注意空间问题,要用滚动数组

时间复杂度:\(O(nm)\)

代码

#include <bits/stdc++.h>
#define Mod 10007
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, m, a[N], s[N], p[N], f[N], sum[N];
bool check(int mid) {
	int sum = 0, tot = 0;
	for (int i = 1; i <= n; i++)
		if (sum + a[i] > mid) tot++, sum = a[i];
			else sum += a[i];
	return tot <= m;
}
int calc() {
	int l = 0, r = 0, ret = 0;
	for (int i = 1; i <= n; i++)
		r += a[i], chkmax(l, a[i]);
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) ret = mid, r = mid - 1;
			else l = mid + 1;
	}
	return ret;
}
int getpos(int x, int sum) {
	int l = 0, r = x, ans = x;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (s[x] - s[mid] <= sum) ans = mid, r = mid - 1;
			else l = mid + 1;
	}
	return ans;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[i]), s[i] = s[i - 1] + a[i];
	int ans1 = calc(), ans2 = 0;
	for (int i = 1; i <= n; i++) p[i] = getpos(i, ans1);
	fill(sum, sum + n + 1, 1);
	for (int j = 1; j <= m + 1; j++) {
		for (int i = 1; i <= n; i++) f[i] = (sum[i - 1] - sum[p[i] - 1] + Mod) % Mod;
		sum[0] = 0;
		for (int i = 1; i <= n; i++) sum[i] = (sum[i - 1] + f[i]) % Mod;
		ans2 = (ans2 + f[n]) % Mod;
	}
	cout << ans1 << ' ' << ans2 << "\n";
	return 0;
}

posted @ 2018-08-14 18:28  谜のNOIP  阅读(86)  评论(0编辑  收藏