[POI 2015]Kinoman
Posted NaVi_Awson
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POI 2015]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<=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
2 3 1 1 4 1 2 4 1
5 3 6 6
Sample Output
15
Hint
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。题解
这道题稀里糊涂的写了一个下午,思路什么的乱七八糟的...回家理清思路,半个小时就写完了。
我们枚举区间左端点,线段树维护每个位置作为右端点的答案,
$nex[i]$记录第$i$天的电影下次播放时间
当我们往左移的时候,显然$i$这个点的颜色已经不会为之后的区间有加成,我们需要将$i$~$nex[i]-1$这一段区间减掉$w[f[i]]$。
同样,当我们左端点左移的时候,将迎接一个新的$f[i]$,那么我们就要将$nex[i]$~$nex[nex[i]]-1$这一段区间加上$w[f[i]]$。
线段树维护,支持区间修改以及查询最大值。
注意一开始的时候就把所有颜色的最左的一段加入线段树中。
1 //It is made by Awson on 2017.10.16 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <cmath> 7 #include <stack> 8 #include <queue> 9 #include <vector> 10 #include <string> 11 #include <cstdio> 12 #include <cstdlib> 13 #include <cstring> 14 #include <iostream> 15 #include <algorithm> 16 #define LL long long 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 #define Max(a, b) ((a) > (b) ? (a) : (b)) 19 #define sqr(x) ((x)*(x)) 20 #define Lr(o) (o<<1) 21 #define Rr(o) (o<<1|1) 22 using namespace std; 23 const int N = 1000000; 24 void read(int &x) { 25 char ch; bool flag = 0; 26 for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == ‘-‘)) || 1); ch = getchar()); 27 for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); 28 x *= 1-2*flag; 29 } 30 31 struct segment { 32 LL sgm[(N<<2)+5], lazy[(N<<2)+5]; 33 void pushdown(int o) { 34 sgm[Lr(o)] += lazy[o], sgm[Rr(o)] += lazy[o]; 35 lazy[Lr(o)] += lazy[o], lazy[Rr(o)] += lazy[o]; 36 lazy[o] = 0; 37 } 38 void update(int o, int l, int r, int a, int b, int key) { 39 if (a <= l && r <= b) { 40 sgm[o] += key, lazy[o] += key; 41 return; 42 } 43 pushdown(o); 44 int mid = (l+r)>>1; 45 if (a <= mid) update(Lr(o), l, mid, a, b, key); 46 if (b > mid) update(Rr(o), mid+1, r, a, b, key); 47 sgm[o] = Max(sgm[Lr(o)], sgm[Rr(o)]); 48 } 49 }T; 50 51 int n, m, f[N+5], w[N+5]; 52 int path[N+5], nex[N+5]; 53 54 void work() { 55 read(n), read(m); 56 for (int i = 1; i <= n; i++) read(f[i]); 57 for (int i = 1; i <= m; i++) read(w[i]); 58 for (int i = n; i >= 1; i--) { 59 nex[i] = path[f[i]], path[f[i]] = i; 60 } 61 for (int i = 1; i <= m; i++) if (path[i]) { 62 if (nex[path[i]]) T.update(1, 1, n, path[i], nex[path[i]]-1, w[i]); 63 else T.update(1, 1, n, path[i], n, w[i]); 64 } 65 LL ans = 0; 66 for (int i = 1; i <= n; i++) { 67 ans = Max(ans, T.sgm[1]); 68 if (nex[i]) { 69 T.update(1, 1, n, i, nex[i]-1, -w[f[i]]); 70 if (nex[nex[i]]) T.update(1, 1, n, nex[i], nex[nex[i]]-1, w[f[i]]); 71 else T.update(1, 1, n, nex[i], n, w[f[i]]); 72 }else T.update(1, 1, n, i, n, -w[f[i]]); 73 } 74 printf("%lld\n", ans); 75 } 76 int main() { 77 work(); 78 return 0; 79 }
以上是关于[POI 2015]Kinoman的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 3747 3747: [POI2015]Kinoman (线段树)