[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\n", Ans);
    return 0;
}
时间: 2024-10-29 19:07:58

[BZOJ 3747] [POI 2015] Kinoman【线段树】的相关文章

BZOJ 3747 POI 2015 Kinoman 线段树

题目大意:给出电影院的放映电影顺序,一个电影只有看过一次的时候会获得电影的权值.没看过或者看两次或以上都不能获得权值.问看连续区间的电影能够获得的最大权值是多少. 思路:利用线段树维护前缀和.将出现第一次的地方的权值加上那部电影的权值,第二次出现的时候权值减去那部电影的权值.枚举起点,先更新答案,然后在当前节点减去权值的二倍,然后再在下一次出现的地方加上权值(我感觉我没说明白,总之看代码吧... CODE: #include <cstdio> #include <cstring>

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i

3747: [POI2015]Kinoman|线段树

枚举左区间线段树维护最大值 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue> #include<set> #include<map> #define ll l

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

【BZOJ3747】[POI2015]Kinoman 线段树

[BZOJ3747][POI2015]Kinoman Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值.所以你希望最大化观看且仅观看过一次的电影的好看值的总和. Input 第一行两个整数n,m(1<=m<=n&

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

BZOJ 3196 二逼平衡树 线段树+treap

题意:链接 方法:线段树+treap的模板题 题解: 首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已. #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,