题目大意:给出电影院的放映电影顺序,一个电影只有看过一次的时候会获得电影的权值。没看过或者看两次或以上都不能获得权值。问看连续区间的电影能够获得的最大权值是多少。
思路:利用线段树维护前缀和。将出现第一次的地方的权值加上那部电影的权值,第二次出现的时候权值减去那部电影的权值。枚举起点,先更新答案,然后在当前节点减去权值的二倍,然后再在下一次出现的地方加上权值(我感觉我没说明白,总之看代码吧。。。
CODE:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 1000010 using namespace std; #define LEFT (pos << 1) #define RIGHT (pos << 1|1) struct SegTree{ long long _max,c; }tree[MAX << 2]; int total,cnt; int src[MAX],value[MAX]; int first[MAX],last[MAX],next[MAX]; inline void PushDown(int pos) { if(tree[pos].c) { tree[LEFT]._max += tree[pos].c; tree[RIGHT]._max += tree[pos].c; tree[LEFT].c += tree[pos].c; tree[RIGHT].c += tree[pos].c; tree[pos].c = 0; } } inline void Modify(int l,int r,int x,int y,int c,int pos) { if(l == x && y == r) { tree[pos]._max += c; tree[pos].c += c; return ; } PushDown(pos); int mid = (l + r) >> 1; if(y <= mid) Modify(l,mid,x,y,c,LEFT); else if(x > mid) Modify(mid + 1,r,x,y,c,RIGHT); else { Modify(l,mid,x,mid,c,LEFT); Modify(mid + 1,r,mid + 1,y,c,RIGHT); } tree[pos]._max = max(tree[LEFT]._max,tree[RIGHT]._max); } inline long long Ask(int l,int r,int x,int y,int pos) { if(l == x && y == r) return tree[pos]._max; PushDown(pos); int mid = (l + r) >> 1; if(y <= mid) return Ask(l,mid,x,y,LEFT); if(x > mid) return Ask(mid + 1,r,x,y,RIGHT); long long left = Ask(l,mid,x,mid,LEFT); long long right = Ask(mid + 1,r,mid + 1,y,RIGHT); return max(left,right); } int main() { cin >> total >> cnt; for(int i = 0; i < MAX; ++i) next[i] = total + 1; for(int i = 1; i <= total; ++i) { scanf("%d",&src[i]); if(!first[src[i]]) { first[src[i]] = i; last[src[i]] = i; } else { next[last[src[i]]] = i; last[src[i]] = i; } } for(int i = 1; i <= cnt; ++i) scanf("%d",&value[i]); for(int i = 1; i <= cnt; ++i) if(first[i]) { Modify(1,total + 1,first[i],total + 1,value[i],1); Modify(1,total + 1,next[first[i]],total + 1,-value[i],1); } long long ans = 0; for(int i = 1; i <= total; ++i) { ans = max(ans,Ask(1,total,i,total,1)); Modify(1,total + 1,i,total + 1,-value[src[i]],1); Modify(1,total + 1,next[i],total + 1,value[src[i]] << 1,1); Modify(1,total + 1,next[next[i]],total + 1,-value[src[i]],1); } cout << ans << endl; return 0; }
BZOJ 3747 POI 2015 Kinoman 线段树
原文地址:http://blog.csdn.net/jiangyuze831/article/details/41651811