@codeforces - 455Eの@関数


@説明@

既知の配列、及び以下の関係所与:
\ [\ケース{F}始める= a_j&\\ F(I、J)= \分\ F {(1、J)(1 \ルJ \ N-LE)。 (I - 1、j)は、 F(I - 1、J - 1)\} + a_j&(2 \ルI \ルJ \ルN)\端{ケース} \]

F(XI、YI)値を求めてM回与えられたクエリ(XI、YI)。

入力
最初の行の整数n(1≤nは10 ^≤ 5) - 配列のサイズ。
次ラインnの整数[1]、[2 ]、...、[N](0≤[i]は10 ^ 4≤)。
次の行整数m(^ 10≤1≤M 5)、 問い合わせの数。
次のm行は、2つの整数XI、YI(1≤XIの≤の含有李≤n)を、 値が要求F(XI、YI)のを表します。

出力
出力m行、対応するクエリ応答によって表される各。

実施例
インプット
6
2 2 3 3 4
4
4 5
3 4
3 4
2 3
出力
12
9
9
5

入力
7
1 3 2 3 4 0 2
4
4
2 3
1 4
4 6
出力
11
4
3
0

@解決@

考えてみましょうそれは本当の意味のDPの式fのように見える(i、j)は次のとおりです。
連続区間[X、J]のために選択された開始前jから、確実にするためにその[X]〜[J ] 少なくとも一度選択されています私は合計数を選出します。そして数と、この私の最小化。

そして、分析の本質を考えます。
[X]〜[Jに加えて 】 これらの固定選択のうちの少なくとも1つから選択され、他の利用可能な選択は、選択すべき[X、j]は最小です。
あなたは[x]は最小値ではないと言うなら、私たちは、[x]は最小値で使用可能な追加オプションを削除するには置くことができます。したがって、特定の最小値は、エンドポイントを残しました。
S [X - Sは、プレフィックスと、この時のSのコスト[J]と呼ばれる -1] +(I - (J-X + 1))* [X]。
左端点は、最小値は、確かに優れていないではありませんので。だから我々はSに質問[j]を頼む- S [X -1] +(I - (J-X + 1))* [x]の最小値(J-I + 1 <= X <= J)

S [X - S [j]が注目 -1] +(I - (J-X + 1)が)* [x]は、古典的なタイプの傾きである(J - I)のように書くことができる。* [ X] +(B - S [J] )=(X - 1)* [X] - S [X-1]、 bの値を最小にします。
点の集合に対して接線方向の直線([x]は、(x - I、Jが固定されている場合、すべての時間は、我々は、(i j)に傾斜相当取得 - 1)* [X] - S [X-1] )(J-I + 1 < = xで<= j)は、 最小の切片を検索します。

あなたがお問い合わせの範囲内でそのすべての凸包を維持したい場合は、問い合わせのみOを必要とするが、それの半分を(ログ)が、非常に複雑の凸包を構築します。
凸包を対応するノードを維持するために森林限界のセグメントで、オンラインのセグメントは、ログ照会に分割された各ノードを尋ね、木:私たちは、複雑さについてのバランスを考慮することを望むかもしれません。
このように、クエリの複雑さの複雑さと凸包上に構築はO(nlog ^ 2N)Aに平衡化されています。

@acceptedコード@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define lch (x<<1)
#define rch (x<<1|1)
typedef long long ll;
const int MAXN = 100000;
struct point{
    ll x, y;
    point(ll _x=0, ll _y=0):x(_x), y(_y) {}
    friend bool operator < (point a, point b) {
        return (a.x == b.x) ? a.y < b.y : a.x < b.x;
    }
};
long double slope(point a, point b) {
    return ((long double)(b.y - a.y)) / (b.x - a.x);
}
int s[MAXN + 5], a[MAXN + 5], n, m;
int le[4*MAXN + 5], ri[4*MAXN + 5];
vector<point>A[4*MAXN + 5], tmp;
void build(int x, int l, int r) {
    le[x] = l, ri[x] = r; tmp.clear();
    for(int i=l;i<=r;i++)
        tmp.push_back(point(a[i], -s[i-1] + (i-1)*a[i]));
    sort(tmp.begin(), tmp.end());
    for(int i=0;i<tmp.size();i++) {
        if( !A[x].size() ) A[x].push_back(tmp[i]);
        else {
            if( tmp[i].x != A[x][A[x].size() - 1].x ) {
                while( A[x].size() >= 2 ) {
                    int m = A[x].size();
                    if( slope(A[x][m-2], A[x][m-1]) >= slope(A[x][m-1], tmp[i]) ) A[x].pop_back();
                    else break;
                }
                A[x].push_back(tmp[i]);
            }
        }
    }
    if( l == r ) return ;
    int mid = (l + r) >> 1;
    build(lch, l, mid), build(rch, mid + 1, r);
}
ll query(point a, int k) {return a.y - k*a.x;}
ll query(const vector<point>&v, int k) {
    int l = 0, r = v.size() - 1;
    while( l < r ) {
        int mid = (l + r) >> 1;
        if( slope(v[mid], v[mid + 1]) >= k ) r = mid;
        else l = mid + 1;
    }
    return query(v[r], k);
}
ll query(int x, int l, int r, int k) {
    if( l <= le[x] && ri[x] <= r )
        return query(A[x], k);
    int mid = (le[x] + ri[x]) >> 1;
    if( r <= mid ) return query(lch, l, r, k);
    else if( l > mid ) return query(rch, l, r, k);
    else return min(query(lch, l, r, k), query(rch, l, r, k));
}
int main() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i]), s[i] = s[i-1] + a[i];
    build(1, 1, n);
    scanf("%d", &m);
    for(int i=1;i<=m;i++) {
        int x, y; scanf("%d%d", &x, &y);
        /*int ans = inf;
        for(int j=y;j>=y-x+1;j--)
            ans = min(ans, int(Y(j) - (y-x)*X(j))); // b = y - kx, y = kx + b.
        printf("%d\n", ans + s[y]);*/
        printf("%lld\n", query(1, y-x+1, y, y-x) + s[y]);
    }
}

@詳細@

複雑さの凸包を構築する合成二凸包複雑さと同等、すなわち適していない凸包を合併。
これは、凸包セグメントツリーがマージされたデータ構造のような大きな依存情報(この問題はなく、マージされた凸包セグメントツリーを使用する、分解呼び掛けセグメントツリーの使用である)には適していないことができます。
ある意味では、凸包に非常に適してブロック。

おすすめ

転載: www.cnblogs.com/Tiw-Air-OAO/p/11693859.html