[OJ#15]TR #2 画心
Posted xjr_01
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OJ#15]TR #2 画心相关的知识,希望对你有一定的参考价值。
[OJ#15]TR #2 画心
试题描述
渠是一名画师。渠有一支神奇的画笔,可以画尽因果。
渠要画一幅画,这幅画由N个线段组成,线段从1开始编号,第i条线段有一个特殊的因果值Ai。
由于画太长了,渠不可能一次画完,于是渠打算将这N条线段分成若干组来画,每一组的长度要求在[L,R]之间,且必须是编号连续的一段。
对于因果值之和为x的一组线段,渠画完后种下的因果为ax2+bx+c。渠想知道渠将这N条线段画完,最多收获多少因果呢?
输入
输入共三行。
第一行为4个整数N,a,b,c,表示线段的数目与给定的参数。
第二行为2个正整数L,R,为每组长度限制的区间。
第三行为N个整数Ai,分别表示每一条线段的因果值。
输出
仅一行,为最多收获的因果数,保证答案在long long范围之内。
输入示例
7 -1 0 3 1 3 1 -2 3 -4 5 6 -6
输出示例
9
数据规模及约定
对于20%的数据 : 1≤N≤10。
对于40%的数据 : 1≤N≤103。
另有20%的数据 : L=1, R=N, 0≤Ai≤105。
另有20%的数据 : L=1, R=N。
对于100%的数据 : 1≤N≤52501, 0≤b,c≤52501, 0≤|Ai|≤105, ?10<a<0。
题解
新学一个叫线段树分治。
首先设 f(i) 表示前 i 条线段能获得的最大分数(由于“因果值”太奇怪,改称分数),那么直接可以得出转移方程(设数列 A 的前缀和为数列 S)
f(i) = max{ f(j) + a(Si - Sj)2 + b(Si - Sj) + c | j ∈ [i-R, i-L]∪[0, n] }
这题推推式子发现是斜率优化题,但是由于 A 中元素有正有负,顾数列 S 无单调性,并且会发现斜率也没有单调性,所以不能简单处理了。
这时候我们考虑一个点能转移到哪,显然也是一个区间(即 f(j) 可以转移到 [j+L, j+R] 这个区间),于是我们可以在线段树上 [j+L, j+R] 所对应的 log(n) 个区间加上 j 的标记;那么对于线段树某个节点,它上面所有标记都适用于它子树中所有的节点(即这个节点上的标记可以转移到所有子节点所对应的位置)。
然后我们就可以按 DFS 序遍历这棵线段树;考虑遍历到某个节点时,该节点上的标记一定在该节点所表示区间的左边,由于我们是按照 DFS 序处理的,所以该节点上的标记一定都已经得出了答案,所以就可以直接用这些标记离线维护凸包;在处理叶子结点时,沿着它到根的路径在所有节点上的凸包进行二分,得到的所有答案中取最大值。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <vector> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); } return x * f; } #define maxn 52510 #define maxlog 64 #define LL long long #define LD long double const LD oold = (LD)1.0 / 0.0; int n, a, b, c, L, R; struct Vec { LD x, y; int id; Vec() {} Vec(LD _1, LD _2, int _3): x(_1), y(_2), id(_3) {} Vec operator - (const Vec& t) const { return Vec(x - t.x, y - t.y, id); } LD operator ^ (const Vec& t) const { return x * t.y - y * t.x; } bool operator < (const Vec& t) const { return x < t.x; } } ps[maxn]; int head[maxn<<2], ToT, nxt[maxn*maxlog], need[maxn*maxlog]; void target(int o, int l, int r, int ql, int qr, int id) { if(ql <= l && r <= qr) need[++ToT] = id, nxt[ToT] = head[o], head[o] = ToT; else { int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(ql <= mid) target(lc, l, mid, ql, qr, id); if(qr > mid) target(rc, mid + 1, r, ql, qr, id); } return ; } LD val[maxn<<2], f[maxn], S[maxn]; int q[maxn], top; vector <int> poly[maxn<<2]; LD calc(int j, int i) { // f[j] -> f[i], return f[i] return (LD)f[j] + (LD)a * (S[i] - S[j]) * (S[i] - S[j]) + (LD)b * (S[i] - S[j]) + c; } void solve(int o, int l, int r) { int mid = l + r >> 1, lc = o << 1, rc = lc | 1, cp = 0; for(int e = head[o]; e; e = nxt[e]) { int j = need[e]; // printf("in vetex %d: %d\n", o, j); ps[++cp] = Vec(S[j], f[j] + (LD)a * S[j] * S[j] - (LD)b * S[j], j); } if(cp) { sort(ps + 1, ps + cp + 1); q[top = 1] = 1; while(q[1] <= cp && ps[q[1]].y == -oold) q[1]++; for(int i = q[1] + 1; i <= cp; i++) { if(ps[i].y == -oold) continue; while(top > 1 && (ps[q[top]] - ps[q[top-1]] ^ ps[i] - ps[q[top]]) >= 0) top--; q[++top] = i; } if(q[1] > cp) top = 0; while(top && ps[q[top]].y == -oold) top--; if(top) for(int i = 1; i <= top; i++) poly[o].push_back(ps[q[i]].id); } if(l == r) { int u = o; while(u) { int _l = 0, _r = poly[u].size() - 1; while(_l < _r) { int _mid = _l + _r >> 1; if(_mid < poly[u].size() - 1 && calc(poly[u][_mid], l) <= calc(poly[u][_mid+1], l)) _l = _mid + 1; else _r = _mid; } if(poly[u].size()) f[l] = max(f[l], calc(poly[u][_l], l)); // , printf("u: %d\n", u); u >>= 1; } // printf("f[%d] = %.0Lf\n", l, f[l]); } else solve(lc, l, mid), solve(rc, mid + 1, r); return ; } int main() { n = read(); a = read(); b = read(); c = read(); L = read(); R = read(); for(int i = 1; i <= n; i++) S[i] = S[i-1] + read(); for(int i = 1; i <= n; i++) f[i] = -oold; for(int i = 0; i <= n; i++) target(1, 0, n, i + L, min(i + R, n), i); solve(1, 0, n); printf("%.0Lf\n", f[n]); return 0; }
以上是关于[OJ#15]TR #2 画心的主要内容,如果未能解决你的问题,请参考以下文章