Posts Tagged ‘HNOI’

HNOI 2008 玩具装箱

Posted in 各省省选 on 五月 19th, 2010 by 纳米 – Be the first to comment

解题报告:
设f(x)为把玩具1至玩具x装箱好的费用,c[x]为玩具x压缩后的长度,有方程:
f(x) = min{f(i) + w[i+1, x] | 0 <= i < x},其中,w[i, j] = (j-i + sum{c[k] | i <= k <= j} – L)^2。
首先证明,w[i, j] + w[i+1, j+1] <= w[i+1, j] + w[i, j+1]:
假定j为常量, i为变量:
w[i, j+1] – w[i, j]
=(j+1-i + sum{c[k] | i <= k <= j}+c[j+1] – L)^2 – (j-i + sum{c[k] | i <= k <= j} – L)^2
=((j-i+sum{c[k] | i <= k <= j}-L) + (c[j+1]+1))^2 – (j-i+sum{c[k] | i <= k <= j}-L)^2
=2(j-i+sum{c[k] | i <= k <= j}-L)(c[j+1]+1)+(c[j+1]+1)^2
容易知道,上述结果关于i单调递减。
同理,w[i, j] – w[i+1, j]关于j单调递增。
所以,w满足w[i, j] + w[i+1, j+1] <= w[i+1, j] + w[i, j+1]

注意,因为我没有数据,此代码并不确保AC。严格来说是一定不能AC的,因为我没有写高精度。

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
#include <stdio.h>
 
const int maxn = 50005;
int n, L, data[maxn], stack[maxn][3], last;
long long sum[maxn], dp[maxn], answer;
 
int input() {
	scanf("%d%d", &n, &L);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &data[i]);
	return 0;
}
 
int work_init() {
	sum[0] = 0;
	for (int i = 1; i <= n; ++i)
		sum[i] = sum[i-1] + data[i];
	return 0;
}
 
long long calc_dp(int i, int j) {
	return dp[j] + (i-j-1 + sum[i]-sum[j] - L) * (i-j-1 + sum[i]-sum[j] - L);
}
 
int work_find(int t0, int t1, int w0, int w1) {
	int left = w0, right = w1, res = w1 + 1;
	while (left <= right) {
		int mid = (left + right) >> 1;
		if (mid > t0 && calc_dp(mid, t0) <= calc_dp(mid, t1)) {
			res = mid;
			right = mid - 1;
		} else {
			left = mid + 1;
		}
	}
	return res;
}
 
int work_dp() {
	dp[0] = last = 0;
	stack[0][0] = 0;
	stack[0][1] = 1;
	stack[0][2] = n;
	int pw = 0;
	for (int i = 1; i <= n; ++i) {
		if (pw > last)
			pw = last;
		while (i > stack[pw][2])
			++pw;
		dp[i] = calc_dp(i, stack[pw][0]);
		while (stack[last][1] > i && calc_dp(stack[last][1], i) <= calc_dp(stack[last][1], stack[last][0]))
			--last;
		int w = work_find(i, stack[last][0], stack[last][1], stack[last][2]);
		if (w <= n) {
			stack[last][2] = w - 1;
			++last;
			stack[last][0] = i;
			stack[last][1] = w;
			stack[last][2] = n;
		}
	}
	answer = dp[n];
	return 0;
}
 
int output() {
	printf("%lld\n", answer);
	return 0;
}
 
int main() {
	input();
	work_init();
	work_dp();
	output();
	return 0;
}