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; } |