首页 > 代码库 > [BZOJ 3747] [POI 2015] Kinoman【线段树】

[BZOJ 3747] [POI 2015] Kinoman【线段树】

Problem Link : BZOJ 3747

 

题解:ZYF-ZYF 神犇的题解

  解题的大致思路是,当区间的右端点向右移动一格时,只有两个区间的左端点对应的答案发生了变化。

  从 f[i] + 1 到 i 的区间中的答案增加了 W[A[i]], 从 f[f[i]] + 1 到 f[i] 的区间的答案减少了 W[A[i]] ,其余区间的答案没有发生变化。

  那么就是线段树的区间修改和区间最值查询。

 

代码如下:

  

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm> using namespace std; const int MaxN = 1000000 + 5int n, m;int A[MaxN], W[MaxN], Last[MaxN], F[MaxN]; typedef long long LL; LL Ans;LL T[MaxN * 4], D[MaxN * 4]; inline LL gmax(LL a, LL b) {    return a > b ? a : b;} inline void Update(int x) {    T[x] = gmax(T[x << 1], T[x << 1 | 1]);} inline void Read(int &num) {    char c; c = getchar();    while (c < 0 || c > 9) c = getchar();    num = c - 0; c = getchar();    while (c >= 0 && c <= 9) {        num = num * 10 + c - 0;        c = getchar();    }} inline void Paint(int x, LL num) {    D[x] += num;    T[x] += num;} inline void PushDown(int x) {    if (D[x] == 0) return;    Paint(x << 1, D[x]);    Paint(x << 1 | 1, D[x]);    D[x] = 0;} LL Add(int x, int s, int t, int l, int r, int num) {    if (l <= s && r >= t) {        Paint(x, (LL)num);        return T[x];    }    PushDown(x);    int m = (s + t) >> 1;    LL ret = 0;    if (l <= m) ret = gmax(ret, Add(x << 1, s, m, l, r, num));    if (r >= m + 1) ret = gmax(ret, Add(x << 1 | 1, m + 1, t, l, r, num));    Update(x);    return ret;} int main() {    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; i++) {        Read(A[i]);        F[i] = Last[A[i]];        Last[A[i]] = i;    }    for (int i = 1; i <= m; i++) Read(W[i]);    Ans = 0;            for (int i = 1; i <= n; i++) {        Ans = gmax(Ans, Add(1, 1, n, F[i] + 1, i, W[A[i]]));        if (F[i] != 0) Ans = gmax(Ans, Add(1, 1, n, F[F[i]] + 1, F[i], -W[A[i]]));    }    printf("%lld\n", Ans);    return 0;}

 

[BZOJ 3747] [POI 2015] Kinoman【线段树】