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();
}
posted @ 2020-05-28 21:36  Cold_Chair  阅读(780)  评论(2编辑  收藏  举报