# BZOJ 2726: [SDOI2012]任务安排( dp + cdq分治 )

考虑每批任务对后面任务都有贡献, dp(i) = min( dp(j) + F(i) * (T(i) - T(j) + S) ) (i < j <= N)  F, T均为后缀和. 与j有关的量只有t = dp(j) - F(i) * T(j) , 我们要最小化它. dp(j)->y, T(j)->x, 那么y = F(i) * x + t, 就是给一些点和一个斜率...然后最小化截距, 显然维护下凸包就可以了. 然后因为无比坑爹的出题人....时间可以为负数, 所以要用平衡树维护(假如时间为非负数用单调队列就行了)....或者cdq分治. O(N log N)平衡树维护大家都应该会...cdq分治就是对于[l, r), m=(l+r)/2, 处理[m, r)的dp值对[l, m)dp值的贡献(这道题是从后往前dp). 具体就是暴力建[m, r)的凸包, 然后[l, m)的按斜率排序, 依次询问. 预处理一下, 时间复杂度就是O(N log N)了, 空间复杂度是O(N).

-------------------------------------------------------------------------------------------------

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;

const int maxn = 300009;

int N, S, V[maxn], q[maxn], stk[maxn], T[maxn], F[maxn];
ll dp[maxn];

void Init() {
scanf("%d%d", &N, &S);
for(int i = 0; i < N; i++) {
scanf("%d%d", T + i, F + i);
q[i] = N - i - 1;
}
T[N] = F[N] = 0;
for(int i = N; i--; )
T[i] += T[i + 1], F[i] += F[i + 1];
}

bool chk(int a, int b, int c) {
int xl = T[b] - T[a], xr = T[c] - T[b];
ll yl = dp[b] - dp[a], yr = dp[c] - dp[b];
return ((xl < 0) ^ (xr < 0)) ? yl * xr <= yr * xl : yl * xr >= yr * xl;
}

bool Jud(int a, int b, int k) {
ll x = T[b] - T[a], y = dp[b] - dp[a];
return x < 0 ? y > x * k : y < x * k;
}

ll calc(int x, int y) {
return dp[y] + ll(F[x]) * (T[x] - T[y] + S);
}

// [l, r)
void cdq(int l, int r) {
if(l + 1 == r) return;
int m = (l + r) >> 1;
int ql = l, qr = m;
for(int i = l; i < r; i++)
q[i] < m ? V[ql++] = q[i] : V[qr++] = q[i];
for(int i = l; i < r; i++) q[i] = V[i];
cdq(m, r);
int h = 0, t = -1;
for(int i = m; i < r; i++) {
while(t > 0 && chk(stk[t - 1], stk[t], q[i])) t--;
stk[++t] = q[i];
}
for(int i = l; i < m; i++) {
while(h < t && Jud(stk[h], stk[h + 1], F[q[i]])) h++;
dp[q[i]] = min(dp[q[i]], calc(q[i], stk[h]));
}
cdq(l, m);
ql = l, qr = m;
for(int i = l; i < r; i++) if(ql >= m) {
V[i] = q[qr++];
} else if(qr >= r) {
V[i] = q[ql++];
} else
V[i] = T[q[ql]] < T[q[qr]] ? q[ql++] : q[qr++];
for(int i = l; i < r; i++) q[i] = V[i];
}

void Work() {
for(int i = 0; i < N; i++)
dp[i] = ll(F[i]) * (T[i] + S);
cdq(0, N);
printf("%lld\n", dp[0]);
}

int main() {
Init();
Work();
return 0;
}

-------------------------------------------------------------------------------------------------

## 2726: [SDOI2012]任务安排

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 660  Solved: 171
[Submit][Status][Discuss]

5 1
1 3
3 2
4 3
2 3
1 4

153

## Source

posted @ 2016-02-06 18:18  JSZX11556  阅读(791)  评论(2编辑  收藏