@bzoj - [email 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 天内所有的电影。

最大化观看且仅观看过一次的电影的好看值的总和。

input

第一行两个整数 n, m (1 <= m <= n <= 1000000)。

第二行包含 n 个整数 f[1], f[2], …, f[n](1 <= f[i] <= m)。

第三行包含 m 个整数 w[1], w[2], …, w[m](1 <= w[j] <= 1000000)。

output

输出观看且仅观看过一次的电影的好看值的总和的最大值。

sample input

9 4

2 3 1 1 4 1 2 4 1

5 3 6 6

sample output

15

@[email protected]

和 GSS2 一样的套路啊……

从左往右扫描每一个点,同时维护一个线段树。

当扫描到点 i,线段树的每一个点 j 表示以 j 为左端点,i 为右端点的区间权值。

这样,在扫描的时候维护线段树的最值并作适当的修改,就可以求出答案。

@accepted [email protected]

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1000000;
struct node{
    int le, ri;
    ll mx, del;
}tree[4*MAXN + 5];
void build(int x, int l, int r) {
    tree[x].le = l, tree[x].ri = r;
    tree[x].mx = tree[x].del = 0;
    if( l == r ) return ;
    int mid = (l + r) >> 1;
    build(x<<1, l, mid);
    build(x<<1|1, mid+1, r);
}
void pushdown(int x) {
    if( tree[x].del ) {
        tree[x<<1].del += tree[x].del;
        tree[x<<1].mx += tree[x].del;
        tree[x<<1|1].del += tree[x].del;
        tree[x<<1|1].mx += tree[x].del;
        tree[x].del = 0;
    }
}
void pushup(int x) {
    tree[x].mx = max(tree[x<<1].mx, tree[x<<1|1].mx);
}
void modify(int x, int l, int r, ll val) {
    if( l > tree[x].ri || r < tree[x].le )
        return ;
    if( l <= tree[x].le && tree[x].ri <= r ) {
        tree[x].del += val, tree[x].mx += val;
        return ;
    }
    pushdown(x);
    modify(x<<1, l, r, val);
    modify(x<<1|1, l, r, val);
    pushup(x);
}
ll query(int x, int l, int r) {
    if( l > tree[x].ri || r < tree[x].le )
        return -1;
    if( l <= tree[x].le && tree[x].ri <= r )
        return tree[x].mx;
    pushdown(x);
    return max(query(x<<1, l, r), query(x<<1|1, l, r));
}
int f[MAXN + 5], n, m; ll w[MAXN + 5];
int pre[MAXN + 5], adj[MAXN + 5];
int main() {
    ll ans = 0;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++)
        scanf("%d", &f[i]);
    for(int i=1;i<=m;i++)
        scanf("%lld", &w[i]);
    for(int i=1;i<=n;i++) {
        pre[i] = adj[f[i]];
        adj[f[i]] = i;
    }
    build(1, 1, n);
    for(int i=1;i<=n;i++) {
        if( pre[i] )
            modify(1, pre[i]+1, i, w[f[i]]), modify(1, pre[pre[i]]+1, pre[i], -w[f[i]]);
        else modify(1, 1, i, w[f[i]]);
        ans = max(ans, query(1, 1, i));
    }
    printf("%lld\n", ans);
}

@[email protected]

老师让我刷 POI 的题准备在冬令营前给其他人讲解。

果不其然,他忘记了这件事,我也忘记了这件事。

嘛……这几天利用零零散散的时间终于大概补齐了 bzoj 上 POI2015 的题目。

还是有一些思维难度不错的题目,故写一系列博客纪念一下。

就这道题而言……真的差不多是 GSS2 的套路重现……

所以应该没什么实现细节。

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

时间: 2024-10-17 08:02:31

@bzoj - [email protected] [POI2015] Kinoman的相关文章

@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],那么这个人就不洗车了. 请给每家店指定一个

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

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]. Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]. 对于一次行走,假设起点为 x,终点为

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

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]. 相邻两份食物之间坐着一个人,共有 n 个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他 n-1 个人的选

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

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一张 n 个点 m 条边的带权有向图,每条边的边权只可能是1,2,3中的一种. 将所有可能的路径按路径长度排序,请输出第 k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点. input 第一行包含三个整数 n, m, k (1<=n<=40,1<=m<

@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