BZOJ3747Kinoman题解--线段树

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=3747

分析

不敢相信这居然是线段树题…还是太菜了

我们钦定第$i$天的电影一定去看(也就是说区间右端点为$i$).那么对于第$i$天放映的电影记录其上一次放该电影的日期为第$lst[i]$天,那么显然这天放映的电影一个做出贡献的区间为$[lst[i]+1,i]$,当然第$i$天放映的那部电影对$[lst[lst[i]]+1,lst[i]]$的贡献就得减去

于是乎如果我们这时候查询$[1,i]$这个区间的最大值,就可以获得区间右端点为$i$的最大收益.所以我们对于每个$i$都试一遍就好了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#define ll long long
#define ri register int
using std::min;
using std::max;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=1000005;
const int inf=0x7fffffff;
int n,m;
int L,R;ll dta;
ll mx[maxn<<2],tag[maxn<<2];
inline void pushdown(int now){
if(tag[now]){
mx[now<<1]+=tag[now],mx[now<<1|1]+=tag[now];
tag[now<<1]+=tag[now],tag[now<<1|1]+=tag[now];
tag[now]=0;
}
return ;
}
void update(int now,int l,int r){
if(L<=l&&r<=R){
mx[now]+=dta,tag[now]+=dta;return ;
}
int mid=(l+r)>>1;
pushdown(now);
if(L<=mid)update(now<<1,l,mid);
if(mid<R)update(now<<1|1,mid+1,r);
mx[now]=max(mx[now<<1],mx[now<<1|1]);
return ;
}
ll ans=0;
void query(int now,int l,int r){
if(L<=l&&r<=R){ans=max(ans,mx[now]);return ;}
int mid=(l+r)>>1;pushdown(now);
if(L<=mid)query(now<<1,l,mid);
if(mid<R)query(now<<1|1,mid+1,r);
return ;
}
int f[maxn],w[maxn],lst[maxn],pos[maxn];
int main(){
read(n),read(m);
for(ri i=1;i<=n;i++){
read(f[i]);lst[i]=pos[f[i]],pos[f[i]]=i;
}
for(ri i=1;i<=m;i++)read(w[i]);
for(ri i=1;i<=n;i++){
if(lst[i]){
L=lst[lst[i]]+1,R=lst[i],dta=-w[f[i]];
update(1,1,n);
}
L=lst[i]+1,R=i,dta=w[f[i]];
update(1,1,n);
L=1,R=i;
query(1,1,n);
}
printf("%lld\n",ans);
return 0;
}