@codeforces - [email protected] Function

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

已知 a 序列,并给定以下关系:
\[\begin{cases}
f(1, j) = a_j & (1 \le j \le n) \f(i, j) = \min\{f(i - 1, j), f(i - 1, j - 1)\} + a_j & (2 \le i \le j \le n)
\end{cases}\]

给定 m 次询问 (xi, yi),求 f(xi, yi) 的值。

Input
第一行一个整数 n (1?≤?n?≤?10^5) — a 序列的大小。
接下来一行 n 个整数 a[1],?a[2],?...,?a[n] (0?≤?a[i]?≤?10^4)。
接下来一行一个整数 m (1?≤?m?≤?10^5),询问的个数。
接下来 m 行每行包含两个整数 xi, yi (1?≤?xi?≤?yi?≤?n),表示求 f(xi, yi) 的值。

Output
输出 m 行,表示每组询问对应的答案。

Examples
Input
6
2 2 3 4 3 4
4
4 5
3 4
3 4
2 3
Output
12
9
9
5

Input
7
1 3 2 3 4 0 2
4
4 5
2 3
1 4
4 6
Output
11
4
3
0

@[email protected]

考虑那个看起来像 dp 的式子 f(i, j) 的实际意义:
从 j 开始往前选一段连续的区间 [x, j],保证 a[x] ~ a[j] 都至少被选一次,选出共 i 个数。并求这 i 个数之和的最小值。

然后考虑分析性质。
除了 a[x] ~ a[j] 都至少选一次这些固定的选择,其他可用的选择应该都选择 [x, j] 中的最小值。
假如说 a[x] 不是最小值,则我们可以把 a[x] 删掉,将多出来的可用选择用于最小值。因此左端点一定为最小值。
记 s 为 a 的前缀和,则此时代价为 s[j] - s[x-1] + (i - (j-x+1))*a[x]。
因为左端点不为最小值时,肯定不优。所以我们问题转为求 s[j] - s[x-1] + (i - (j-x+1))*a[x] 的最小值 (j-i+1 <= x <= j)

注意到 s[j] - s[x-1] + (i - (j-x+1))*a[x] 是一个很经典的斜率式,可以写成 (j - i)*a[x] + (b - s[j]) = (x - 1)*a[x] - s[x-1],最小化 b 的值。
当 i, j 固定时,相当于我们每次拿一个斜率为 (j - i) 的直线去切点集 (a[x], (x - 1)*a[x] - s[x-1]) (j-i+1 <= x <= j),找最小截距。

如果想要去维护出每次询问中区间内的凸包,虽然询问只需要 O(log) 二分一下,但是构建凸包复杂度很大。
我们不妨考虑平衡一下复杂度:将一个询问在线段树上拆成 log 个询问,在线段树的每个结点中维护该结点对应的凸包。
则这样询问复杂度与凸包构建的复杂度就都可以平衡到 O(nlog^2n) 了。

@accepted [email protected]

#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]);
    }
}

@[email protected]

合并两个凸包的复杂度等价于构建凸包的复杂度,即凸包不适合进行合并。
这使得凸包并不大适合线段树等依赖于信息合并的数据结构(本题是利用线段树拆解询问,而不是利用线段树合并凸包)。
但某种意义上来说,凸包挺适合分块的。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11693859.html

时间: 2024-08-29 11:34:48

@codeforces - [email protected] Function的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@hdu - [email&#160;protected] Function

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定 n,求: \[\sum_{i=1}^{n}gcd(\lfloor^3\sqrt{i}\rfloor, i)\mod 998244353\] Input 第一行包含一个整数 T(1≤T≤11) 描述数据组数. 接下来 T 行,每行一个整数 n (1≤n≤10^21) 描述询问. O

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.