今天是第一次写。本来打算抱着二分栈的写法不放,不想学别的写法,但是今天看到主席的代码是我的三分之一还不到,简直比裸的平方dp还要短,我就忍不住去学了。
先直接说这道题的方程:f[i] = max{f[j] + sum(j + 2, i)}, 1 <= i <= n, i - m - 1 <= j <= i - 1。
如果像我原来一样写二分栈的话,j 的限制将使代码变得很不方便,而且跑起来比单调队列慢一倍。
单调队列应该怎么写呢?
首先,把方程变形,得:f[i] - s[i] = max{f[j] - s[j + 1]}(限制条件如上,s[j] 表示前 j 项和)。
这样(很简单啊。。。)就得到了一个只与 j 有关的方程的另一边,记之为 X[j]。
然后如何处理呢?就可以通过维护一个单调队列,来保存所有当前合法的决策,其中从队首到队尾 X 值依次递减。每次通过 pop 队首保证决策的合法,然后用队首的最优决策更新当前的状态,然后每次在队尾加入当前所得的这个决策。加入时,为了维护队列的单调性,每次向前 pop 不如之优的决策。很显然,因为每次都会往队列中至少添加一个决策,而且每次决策前都会 pop 队首不合法决策,所以每次队列中的决策都一定会合法。而每个元素最多被加入队列一次,也最多被 pop 一次,所以整个过程的复杂度是 O(n) 的,完虐二分栈 O(nlogn)。
顺便说一下,由于觉得主席很酷,所以我决定也宏定义 for 循环。
Code :
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef int TYPE;
typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64;
#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define MAX(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define MIN(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})
#define max(a, b) ({TYPE _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b) ({TYPE _ = (a), __ = (b); _ < __ ? _ : __;})
#define FOR(i, l, r) for (int i = (l), _r = (r); i <= _r; ++ i)
#define ROF(i, r, l) for (int i = (r), _l = (l); i >= _l; -- i)
#define FER(u, e, v) for (int e = edge[u], v = to[e]; e; v = to[e = next[e]])
#define maxn 100005
#define front que[head + 1]
#define back que[tail]
int n, m, head, tail;
int a[maxn], que[maxn];
int64 s[maxn], f[maxn], k;
int main()
{
freopen("cg.in", "r", stdin);
freopen("cg.out", "w", stdout);
scanf("%d%d", & n, & m);
FOR(i, 1, n) scanf("%d", & a[i]), s[i] = s[i - 1] + a[i];
que[++ tail] = - 1;
FOR(i, 0, n)
{
while (head < tail && front < i - m - 1) ++ head;
f[i] = f[front] + s[i] - s[front + 1], k = f[i] - s[i + 1];
while (head < tail && k > f[back] - s[back + 1]) -- tail;
que[++ tail] = i;
}
printf("%I64d\n", f[n]);
return 0;
}
再帖我二分栈的代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <iostream>
#include <algorithm>
typedef long long TYPE;
typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64;
#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define MAX(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define MIN(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})
#define max(a, b) ({TYPE _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b) ({TYPE _ = (a), __ = (b); _ < __ ? _ : __;})
#define maxn 100005
#define sum(i, j) (s[j] - s[(i) - 1])
//#define getit(i, j) ({int _ = i; f[_] + sum(_ + 2, j);})
int n, m, head, tail;
int64 a[maxn], s[maxn];
int64 ff[maxn], * f = ff + 1;
struct node{int l, r, k;} que[maxn];
void push(int l, int r, int k)
{
while (tail and (que[tail].r = l - 1) < que[tail].l) -- tail;
que[++ tail] = (node){l, r, k};
}
int pop_head()
{
int ans = que[head + 1].k;
if (++ que[head + 1].l > que[head + 1].r) ++ head;
return ans;
}
void pop_tail()
{
que[tail - 1].r = que[tail].r, -- tail;
que[tail].r = min(que[tail].r, que[tail].k + m + 1);
}
int64 getit(int i, int j)
{
return f[i] + sum(i + 2, j);
}
void update(int i)
{
while (head < tail)
{
int l = max(que[tail].l, i + 2), r = que[tail].r, k = que[tail].k;
if (getit(i, r) < getit(k, r)) break;
if (getit(i, l) > getit(k, l))
if (l == que[tail].l) {pop_tail(); continue;}
else {que[tail].r = i + 1; break;}
for (int mid = l + r >> 1; l < r; mid = l + r >> 1)
getit(i, mid) < getit(k, mid) ? l = mid + 1 : r = mid;
push(l, i + m + 1, i); break;
}
if (head == tail) push(i + 2, i + m + 1, i);
else if (que[tail].r < i + m + 1) push(que[tail].r + 1, i + m + 1, i);
}
int main()
{
freopen("cg.in", "r", stdin);
freopen("cg.out", "w", stdout);
scanf("%d%d", & n, & m);
for (int i = 1; i <= n; ++ i)
scanf("%I64d", & a[i]), s[i] = s[i - 1] + a[i];
push(1, m, - 1), update(0);
for (int i = 1; i <= n; ++ i)
{
int j = pop_head();
f[i] = getit(j, i);
update(i);
}
printf("%I64d\n", f[n]);
return 0;
}