• 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("
    ")
    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
    ", 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
    ", ans[i]);
    }
    
    int main() {
    	freopen("maximize.in", "r", stdin);
    	freopen("maximize.out", "w", stdout);
    	scanf("%d %d", &n, &Q);
    	build();
    	End();
    }
    
  • 相关阅读:
    不等式(一)-Markov与Chebyshev不等式
    决策树学习
    k-NN最近邻算法(k-nearest neighbors algorithm)
    梯度下降(Gradient Descent)数学原理分析与实例
    求解素数
    shell基础命令使用
    安装jenkins
    idea拉取git
    shell常用命令
    linux 安装jdk
  • 原文地址:https://www.cnblogs.com/coldchair/p/12984138.html
Copyright © 2020-2023  润新知