@bzoj - [email protected] 旅行规划

目录

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

@[email protected]

请你维护一个序列,支持两种操作:

(1)某个区间 [x, y] 内的数同时加上一个增量 k。

(2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和。

input

第一行给出一个整数 n。n <= 100000。接下来一行 n 个整数表示序列的初始值。

第三行给出一个整数 m,m <= 100000。接下来 m 行每行一个操作。

(1) 0 x y k:表示给区间 [x, y] 同时加上 k。

(2) 1 x y:询问区间 [x, y]。

output

对于每个询问,输出一个整数表示最大前缀和。

sample input

5

1 8 -8 3 -7

3

1 1 5

0 1 3 6

1 2 4

sample output

9

22

@[email protected]

我们考虑直接维护前缀和序列,则操作(2)就是在查询区间最大值。

而对于操作(1),我们相当于两部分操作:

对于 x <= i <= y,给 i 位置加上 (i-x+1)*k;对于 y < i,给 i 位置加上 (y-x+1)*k。

前一个可以拆成 (-x+1)*k + i*k,是常数 + 系数*位置的形式;后面那个也可以看成这种形式,只是位置前面的系数为 0。

看起来还是不好维护,但是我们可以注意到这样一件事情:对于某一个位置 i,它的值总是形如 k*i + b 的形式。

直线解析式。所以我们考虑用几何方法来维护这种东西。几何方法当然首先想到凸包。

每次操作相当于给区间内所有点的斜率与截距同时加上增量,手算一下会发现这个区间相邻两点之间的斜率也会同时增加 k,这样也就是说这个区间的凸包形状不会变化。

线段树不太好搞(况且这道题时限 50s ),我们考虑使用分块算法来维护凸包。

修改时,散块暴力修改并重构凸包,整块打标记,记录这个区间整体的斜率与截距变化量。

查询时,散块暴力求答案,整块凸包上二分。

时间复杂度 \(O(n\sqrt{n}\log n)\)

@accepted [email protected]

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
const int BLOCK = 320;
const ll INF = (1ll<<62);
ll sp[BLOCK + 5], b1[MAXN + 5], b2[BLOCK + 5];
int le[BLOCK + 5], ri[BLOCK + 5], num[MAXN + 5];
int stk[MAXN + 5], tp[BLOCK + 5], n, m, bcnt = 0;
ll get_ans(int x) {
    return sp[num[x]]*x + b1[x] + b2[num[x]];
}
ll query(int x) {
    int l = le[x], r = tp[x];
    while( l < r ) {
        int mid = (l + r) >> 1;
        if( get_ans(stk[mid]) >= get_ans(stk[mid+1]) ) r = mid;
        else l = mid + 1;
    }
    return get_ans(stk[r]);
}
void push_tag(int x) {
    for(int i=le[x];i<=ri[x];i++)
        b1[i] = get_ans(i);
    sp[x] = b2[x] = 0;
}
void build(int x) {
    tp[x] = le[x] - 1;
    for(int i=le[x];i<=ri[x];i++) {
        while( tp[x] > le[x] && (get_ans(i) - get_ans(stk[tp[x]]))*(stk[tp[x]] - stk[tp[x] - 1]) >= (get_ans(stk[tp[x]])-get_ans(stk[tp[x] - 1]))*(i - stk[tp[x]]) )
            tp[x]--;
        stk[++tp[x]] = i;
    }
}
void init() {
    for(int i=0;i<n;i++) {
        if( i % BLOCK == 0 ) {
            num[i] = (++bcnt);
            le[num[i]] = ri[num[i]] = i;
            sp[num[i]] = b2[num[i]] = 0;
        }
        else ri[num[i] = bcnt]++;
    }
}
int main() {
    scanf("%d", &n); init();
    for(int i=0;i<n;i++)
        scanf("%lld", &b1[i]), b1[i] += b1[i-1];
    for(int i=1;i<=bcnt;i++)
        build(i);
    scanf("%d", &m);
    for(int i=1;i<=m;i++) {
        int op, x, y; ll k;
        scanf("%d%d%d", &op, &x, &y);
        x--, y--;
        if( op == 0 ) {
            scanf("%lld", &k);
            if( num[x] != num[y] ) {
                push_tag(num[x]), push_tag(num[y]);
                for(int i=x;i<=ri[num[x]];i++)
                    b1[i] += k*(i-x+1);
                for(int i=le[num[y]];i<=y;i++)
                    b1[i] += k*(i-x+1);
                for(int i=y+1;i<=ri[num[y]];i++)
                    b1[i] += k*(y-x+1);
                build(num[x]), build(num[y]);
                for(int i=num[x]+1;i<=num[y]-1;i++)
                    sp[i] += k, b2[i] += k*(-x+1);
            }
            else {
                push_tag(num[x]);
                for(int i=x;i<=y;i++)
                    b1[i] += k*(i-x+1);
                for(int i=y+1;i<=ri[num[y]];i++)
                    b1[i] += k*(y-x+1);
                build(num[x]);
            }
            for(int i=num[y]+1;i<=bcnt;i++)
                b2[i] += k*(y-x+1);
        }
        else {
            ll ans = -INF;
            if( num[x] != num[y] ) {
                for(int i=x;i<=ri[num[x]];i++)
                    ans = max(ans, get_ans(i));
                for(int i=le[num[y]];i<=y;i++)
                    ans = max(ans, get_ans(i));
                for(int i=num[x]+1;i<=num[y]-1;i++)
                    ans = max(ans, query(i));
            }
            else {
                for(int i=x;i<=y;i++)
                    ans = max(ans, get_ans(i));
            }
            printf("%lld\n", ans);
        }
    }
}

@[email protected]

突然发现这是我第一次写分块?原来我以前从来没写过这种东西?

把分块的左端点右端点以及每个点属于哪个块先预处理出来感觉比较好写。

并且把块的大小设置为常数也是一个不错的懒人做法(虽然想想都知道这样肯定常数大)。

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

时间: 2024-08-28 04:19:33

@bzoj - [email protected] 旅行规划的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu

@bzoj - [email&#160;protected] Hard Nim

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 堆石子,每堆石子的数量是不超过 m 的一个质数. 两个人玩 nim 游戏,问使后手必胜的初始局面有多少种. 模 10^9 + 7. input 多组数据.数据组数 <= 80. 每组数据一行两个正整数,n 和 m.1 <= n <= 10^9, 2 <= m <

@bzoj - [email&#160;protected] [Cqoi2016]伪光滑数

目录 @description@ @[email protected] @version - [email protected] @version - [email protected] @accepted [email protected] @[email protected] @description@ 若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 \(A_k\),并且满足 \(A_k^k \le N\),\(A_k < 128\),我们就称整数 M 为 N - 伪光

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个

@bzoj - [email&#160;protected] [POI2015] Myjnie

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 家洗车店从左往右排成一排,每家店都有一个正整数价格 p[i]. 有 m 个人要来消费,第 i 个人会驶过第 a[i] 个开始一直到第 b[i] 个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于 c[i],那么这个人就不洗车了. 请给每家店指定一个