浏览器标题切换
浏览器标题切换end
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

BZOJ1233: [Usaco2009Open]干草堆tower

Description

奶牛们讨厌黑暗。 为了调整牛棚顶的电灯的亮度,Bessie必须建一座干草堆使得她能够爬上去够到灯泡 。一共有N大包的干草(1<=N<=100000)(从1到N编号)依靠传送带连续的传输进牛棚来。第i包干草有一个 宽度W_i(1<=w_i<=10000)。所有的干草包的厚度和高度都为1. Bessie必须利用所有N包干草来建立起干草堆,并且按照他们进牛棚的顺序摆放。她可以相放多少包就放 多少包来建立起tower的地基(当然是紧紧的放在一行中)。接下来他可以放置下一个草包放在之前一级 的上方来建立新的一级。注意:每一级不能比下面的一级宽。她持续的这么放置,直到所有的草包都被安 置完成。她必须按顺序堆放,按照草包进入牛棚的顺序。说得更清楚一些:一旦她将一个草包放在第二级 ,她不能将接下来的草包放在地基上。 Bessie的目标是建立起最高的草包堆。

Input

第1行:一个单一的整数N。 第2~N+1行:一个单一的整数:W_i。

Output

第一行:一个单一的整数,表示Bessie可以建立的草包堆的最高高度。

Sample Input

3
1
2
3

Sample Output

2
输出说明:
前两个(宽度为1和2的)放在底层,总宽度为3,在第二层放置宽度为3的。
+----------+
| 3 |
+---+------+
| 1 | 2 |
+---+------+

Solution

要猜一个神仙结论才能优化\(dp\),否则写到死也是\(n^2\)的...。
结论:一定存在一种方案,在保证层数最高的同时,底层宽度最小。证明引用zkw的:

任意取出一个能使层数最高的方案,设有CA层,把其中从下往上每一层最大的块编号记为Ai;任取一个能使底边最短的方案,设有CB层,把其中从下往上每一层最大的块编号记为Bi。
显然A1>=B1,ACB<=BCB,这说明至少存在一个k属于(1,CB),满足Ak-1>=Bk-1且Ak<=Bk。也就是说,方案 A 第K 层完全被方案 B 第K 层包含。
构造一个新方案,第K 层往上按方案 A,往下按方案 B,两边都不要的块放中间当第K 层。新方案的层数与 A 相同,而底边长度与 B 相同。
证毕。

那么现在问题就转化为了倒推,然后要找一个编号最小的点\(j\),满足\(s[j-1]-s[i-1]>=f[j]\)。这个可以单调队列维护\(f[j]-s[j-1]\)\(\min\)即可。弹出队头的话需要更改一下,因为肯定要选一个合法的答案,所以要先看\(l+1\)这个位置可不可行,如果可行就弹出队头(因为要找到的是满足条件的,编号最小的点\(j\),而我们是倒着把点放进单调队列的,所以要找单调队列中最后一个满足条件的点\(j\)来转移)。复杂度\(O(n)\)

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

namespace io {
char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() {
    if(p1 != p2) return *p1++;
    p1 = buf;
    p2 = p1 + fread(buf, 1, 1 << 21, stdin);
    return p1 == p2 ? EOF : *p1++;
}
#define G gc

#ifndef ONLINE_JUDGE
#undef G
#define G getchar
#endif

template<class I>
inline void read(I &x) {
    x = 0; I f = 1; char c = G();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = G(); }
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = G(); }
    x *= f;
}
template<class I>
inline void write(I x) {
    if(x == 0) {putchar('0'); return;}
    I tmp = x > 0 ? x : -x;
    if(x < 0) putchar('-');
    int cnt = 0;
    while(tmp > 0) {
        buf[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while(cnt > 0) putchar(buf[--cnt]);
}

#define in(x) read(x)
#define outn(x) write(x), putchar('\n')
#define out(x) write(x), putchar(' ')

} using namespace io;

#define ll long long
const int N = 100010;

int n, a[N], f[N], g[N], s[N], q[N];

int main() {
	read(n); 
	for(int i = 1; i <= n; ++i) read(a[i]), s[i] = s[i - 1] + a[i];
	int l = 1, r = 1;
	q[1] = n + 1;
	for(int i = n; i; --i) {
		while(l < r && s[q[l + 1] - 1] - s[i - 1] >= f[q[l + 1]]) ++l;
		f[i] = s[q[l] - 1] - s[i - 1]; g[i] = g[q[l]] + 1;
		while(l < r && f[i] - s[i - 1] < f[q[r]] - s[q[r] - 1]) --r;
		q[++r] = i;
	}
	outn(g[1]);
}
posted @ 2019-08-11 15:03  henry_y  阅读(210)  评论(0编辑  收藏  举报