[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 + 5;
 
int 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
", Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/JoeFan/p/4160895.html