BZOJ 3005 体育课

Description

又是一节体育课的时间了,有n个同学排成了一排。他们都很讨厌排在第一个位置的同学,于是后面的同学中比第一个高的都会产生一个高兴值,这个高兴值等于他的身高减去第一个同学的身高。当然比第一个同学矮的同学产生兴奋值为0。

现在体育老师来了,他拥有神奇的魔法,现在他能做如下的三件事:

  1. 询问某段区间高兴值最大的那个是多少。
  2. 把某两个同学交换一下位置。
  3. 选取一段区间的人,把第一个人身高加上t,第二个加上2t,第三个加上3t以此类推。

但是体育老师不会数数,于是他找到你了,对于每一个询问,他要你帮他求出那个值。

Input

第一行两个整数n,m表示有n个人,有m个操作。

第二行n个整数,顺序输入每个人的身高。(身高<=10^8)

接下来m行,每行第一个数位一个type表示是做哪一件事情。

如果type=1,那么接下来有两个整数l,r,表示询问这段区间的最大的高兴值

如果type=2,接下来两个整数a,b,表示交换这两个位置的人

如果type=3,接下来三个整数l,r,t,表示把l个人的升高增加t,l+1个人增加2t…第r个人增加(r-l+1)t, (0<=t<=10000)

Output

对于每个询问按照顺序输出每个操作1的答案。

Sample Input

1
2
3
4
5
6
7
8
9
10 7 
10 9 8 7 6 5 4 3 2 1 
1 4 7 
3 2 4 3 
1 2 3 
1 3 6 
2 1 10 
1 8 10 
1 2 3

Sample Output

1
2
3
4
5
0
4
6
9
13

HINT

对于100的数据:n,m<=100000

Source

SDOI 2012

Solution

分块+凸包+二分。

考虑直接维护原序列。

分块,每块大小size,维护两个标记:tag1 tag2,表示这一块的第i个(i在这块中的位置相对于左端点来说)比a[i]变大了tag1*i+tag2。

然后这题非常强的一个思路是,我们让横坐标表示tag1,纵坐标为a[i]+tag1*i+tag2,把每块内的数字都变为 斜率为i(相对) ,截距为a[i]的若干条直线。

这样我们可以建立这些直线的下凸壳,对于x=tag1的询问,在凸壳上二分即可。

pic

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL INF = 1000000000000000000LL;
const int maxn = 100005;
const int size = 350;
LL ans;
int L[maxn / size + 5], R[maxn / size + 5], con[maxn / size + 5][size + 5], pos[maxn];
double ri[maxn / size + 5][size + 5];
LL a[maxn], k[maxn / size + 5], b[maxn / size + 5];
int cnt[maxn];
inline double GetCrossX(int i, int j) { return double(a[i] - a[j]) / double(j - i); }
inline void update_convex(int x) {
    cnt[x] = 0;
    for (int i = L[x]; i <= R[x]; ++i) {
        while (cnt[x] >= 2 && GetCrossX(i, con[x][cnt[x]]) <= GetCrossX(i, con[x][cnt[x] - 1])) --cnt[x];
        con[x][++cnt[x]] = i;
    }
    for (int i = 1; i < cnt[x]; ++i) ri[x][i] = GetCrossX(con[x][i], con[x][i+1]);
    ri[x][cnt[x]] = INF;
}
inline void bf_modify_a(int l, int r, LL k, LL b) {
    for (int i = l; i <= r; ++i) a[i] += b + k * (i - l);
    update_convex(pos[l]);
}
inline void modify_a(int l, int r, LL val) {
    if (pos[l] == pos[r]) return bf_modify_a(l, r, val, val);
    bf_modify_a(l, R[pos[l]], val, val); bf_modify_a(L[pos[r]], r, val, val * (L[pos[r]] - l + 1));
    for (int i = pos[l] + 1; i < pos[r]; ++i) {
        k[i] += val;
        b[i] += val * (L[i] - l + 1);
    }
}
inline void bf_query(int l, int r) {
    int j = pos[l];
    for (int i = l; i <= r; ++i) ans = max(ans, a[i] + b[j] + k[j] * (i - L[j]));
}
inline void query(int i) {
    int l = 1, r = cnt[i], mid;
    while (l < r) {
        mid = (l + r) >> 1;
        if (ri[i][mid] >= k[i]) r = mid;
        else l = mid + 1;
    }
    ans = max(ans, b[i] + k[i] * (con[i][r] - L[i]) + a[con[i][r]]);
}
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
    int cnt_block = (n + size - 1) / size;
    for (int i = 1; i <= cnt_block; ++i) {
        L[i] = R[i - 1] + 1; R[i] = min(n, L[i] + size - 1);
        for (int j = L[i]; j <= R[i]; ++j) pos[j] = i;
        update_convex(i);
    }
    int ty, x, y;
    LL z;
    while (m--) {
        scanf("%d %d %d", &ty, &x, &y);
        if (x > y) swap(x, y);
        if (ty == 3) {
            scanf("%lld", &z);
            modify_a(x, y, z);
            continue;
        }
        if (ty == 2) {
            int i = pos[x], j = pos[y];
            if (i == j) {
                bf_modify_a(L[i], R[i], k[i], b[i]);
                k[i] = b[i] = 0;
            }
            else {
                bf_modify_a(L[i], R[i], k[i], b[i]);
                k[i] = b[i] = 0;
                bf_modify_a(L[j], R[j], k[j], b[j]);
                k[j] = b[j] = 0;
            }
            swap(a[x], a[y]);
            update_convex(i);
            if (i != j) update_convex(j);
            continue;
        }
        ans = -INF;
        if (pos[x] == pos[y]) bf_query(x, y);
        else {
            bf_query(x, R[pos[x]]); bf_query(L[pos[y]], y);
            for (int i = pos[x] + 1; i < pos[y]; ++i) query(i);
        }
        printf("%lld\n", max(ans - a[1] - b[1], 0ll));
    }
}