300iq Contest 2 H Honorable Mention(凸优化、wqs二分+线段树分治+整体思想)
http://codeforces.com/gym/102331/problem/H
题解:
首先,当\(k\)很小时,有一经典模拟费用流做法:
每次找到最大的子区间,加上它,并把它取反,可以用线段树维护。
但这题\(k\)和\(n\)同阶,需要思考其它的做法。
还可以凸优化dp,二分斜率k后用单调队列就可以\(O((r-l+1)*log~V)\)做一次。
考虑优化一些这个dp,显然可以放到线段树上分治。
那么对于每一个区间需要求出斜率为k时最优选的和和区间个数。
对线段树上每个区间预处理选\(?\)段时的最大和,这个是凸的,所以在上面二分可以得到斜率为k的最优值。
注意每个区间要记录左右边界选了没有(因为合并是如果两个端点都选了可以少一段)
线段树区间的预处理可以用闵科夫斯基和从子区间推来。
这样复杂度是\(O(16*n~log~n+8*(Q*log~V*log~n*log~n))\)
可定TLE了。
注意对同一层二分的查询,可以先排序,到线段树每个区间就可以指针找最小值(指针移动距离不超过\(n log n\)),这样复杂度变为:\(O(16*n~log~n+8*(Q*log~V*log~n)+n~log~n*log~V)\)
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 3.5e4 + 5;
const ll inf = 1225000000ll;
int n, Q;
ll a[N];
#define V vector<ll>
#define si size()
V t[N * 4][2][2];
#define i0 i + i
#define i1 i + i + 1
void gx(V &a, V b, V c) {
int n = b.si - 1, m = c.si - 1;
a.resize(n + m + 1);
ll s = b[0] + c[0]; a[0] = s;
int l = 0, r = 0;
while(l < n && r < m) {
if(b[l + 1] - b[l] > c[r + 1] - c[r]) {
s += b[l + 1] - b[l];
l ++;
} else {
s += c[r + 1] - c[r];
r ++;
}
a[l + r] = s;
}
while(l < n) s += b[l + 1] - b[l], l ++, a[l + r] = s;
while(r < m) s += c[r + 1] - c[r], r ++, a[l + r] = s;
}
void fz(V &a, V b, int op) {
ff(i, 0, b.si) {
a[i] = max(a[i], b[i]);
if(op && i) {
a[i - 1] = max(a[i - 1], b[i]);
}
}
}
void bt(int i, int x, int y) {
if(x == y) {
fo(u, 0, 1) fo(v, 0, 1) {
t[i][u][v].resize(2);
t[i][u][v][0] = t[i][u][v][1] = -inf;
if(u == 0 && v == 0) t[i][u][v][0] = 0;
if(u == 1 && v == 1) t[i][u][v][1] = a[x];
}
return;
}
int m = x + y >> 1;
bt(i0, x, m); bt(i1, m + 1, y);
int len = y - x + 1;
fo(u, 0, 1) fo(v, 0, 1) {
t[i][u][v].resize(len + 1);
fo(j, 0, len) t[i][u][v][j] = -inf * n;
}
fo(u, 0, 1) fo(v, 0, 1) fo(p, 0, 1) fo(q, 0, 1) {
V b;
gx(b, t[i0][u][v], t[i1][p][q]);
fz(t[i][u][q], b, (v && p));
}
}
void build() {
fo(i, 1, n) scanf("%lld", &a[i]);
bt(1, 1, n);
}
struct nod {
int x, y, k;
} b[N];
int L[N], R[N], m[N], as[N];
int d[N];
int cmpd(int x, int y) {
return m[x] > m[y];
}
int l[N * 4][2][2];
int pl, pr;
int mk;
struct P {
ll x; int y;
P(ll _x = 0, int _y = 0) {
x = _x, y = _y;
}
};
P operator + (P a, P b) { return P(a.x + b.x, a.y + b.y);}
bool operator < (P a, P b) { return a.x == b.x ? a.y < b.y : a.x < b.x;}
P f[2], h[2];
int find(V &g, int &l) {
while(l < g.si - 1 && g[l + 1] - g[l] >= mk) l ++;
return l;
}
void gg(P *f, V (*g)[2], int (*l)[2]) {
fo(x, 0, 1) {
h[x] = f[x];
f[x] = P(-inf, -inf);
}
fo(u, 0, 1) fo(v, 0, 1) {
int w = find(g[u][v], l[u][v]);
P e = P(g[u][v][w], w);
fo(x, 0, 1) {
P nf = h[x] + e;
nf.x -= e.y * mk;
f[v] = max(f[v], nf);
if(x == 1 && u == 1) {
nf.y --, nf.x += mk;
f[v] = max(f[v], nf);
}
}
}
}
void ft(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
// pp("%d %d %d mk = %d\n", i, x, y, mk);
gg(f, t[i], l[i]);
return;
}
int m = x + y >> 1;
ft(i0, x, m); ft(i1, m + 1, y);
}
ll ans[N];
void End() {
int js = 0;
fo(i, 1, n) js += abs(a[i]);
fo(i, 1, Q) {
scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].k);
L[i] = -35000, R[i] = js / b[i].k;
}
while(1) {
fo(i, 1, Q) {
m[i] = ((ll) L[i] + R[i]) / 2;
d[i] = i;
}
sort(d + 1, d + Q + 1, cmpd);
memset(l, 0, sizeof l);
int ok = 0;
fo(i, 1, Q) {
int x = d[i];
if(L[x] > R[x]) continue;
ok = 1;
mk = m[x];
pl = b[x].x, pr = b[x].y;
f[0] = P(0, 0); f[1] = P(-inf, inf);
ft(1, 1, n);
f[0] = max(f[0], f[1]);
if(f[0].y >= b[x].k) {
as[x] = m[x];
L[x] = m[x] + 1;
} else {
R[x] = m[x] - 1;
}
}
if(!ok) break;
}
memset(l, 0, sizeof l);
fo(i, 1, Q) d[i] = i, m[i] = as[i];
sort(d + 1, d + Q + 1, cmpd);
fo(i, 1, Q) {
int x = d[i];
mk = as[x];
pl = b[x].x, pr = b[x].y;
f[0] = P(0, 0); f[1] = P(-inf, inf);
ft(1, 1, n);
f[0] = max(f[0], f[1]);
ans[x] = f[0].x + b[x].k * mk;
}
fo(i, 1, Q) pp("%lld\n", ans[i]);
}
int main() {
freopen("maximize.in", "r", stdin);
freopen("maximize.out", "w", stdout);
scanf("%d %d", &n, &Q);
build();
End();
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址