题目链接
http://main.edu.pl/en/archive/oi/22/kin
题目大意
共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
思路
我们可以维护一个长度为
n
的线段树,初始时,线段树中第
为了得到这个初始的线段树,我们可以预处理出每个电影第一次放映的时间
last[i]
,以及第
x
天看的电影的下一次放映时间
然后我们从第2天到第
n
天枚举时间
于是我们只需要维护一个能支持区间修改、区间求最值的线段树即可
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100000
using namespace std;
typedef long long int LL;
LL addtag[MAXN<<2],maxtag[MAXN<<2],ans=0;
void pushdown(int o)
{
addtag[o<<1]+=addtag[o];
addtag[o<<1|1]+=addtag[o];
maxtag[o<<1]+=addtag[o];
maxtag[o<<1|1]+=addtag[o];
addtag[o]=0;
}
void pushup(int o)
{
maxtag[o]=max(maxtag[o<<1],maxtag[o<<1|1]);
}
void add(int o,int L,int R,int ql,int qr,LL val)
{
if(ql<=L&&R<=qr)
{
addtag[o]+=val;
maxtag[o]+=val;
return;
}
int M=(L+R)>>1;
pushdown(o);
if(ql<=M) add(o<<1,L,M,ql,qr,val);
if(qr>M) add(o<<1|1,M+1,R,ql,qr,val);
pushup(o);
}
LL query(int o,int L,int R,int ql,int qr)
{
if(ql<=L&&R<=qr)
return maxtag[o];
pushdown(o);
int M=(L+R)>>1;
LL ans=0;
if(ql<=M) ans=max(ans,query(o<<1,L,M,ql,qr));
if(qr>M) ans=max(ans,query(o<<1|1,M+1,R,ql,qr));
return ans;
}
int n,m,f[MAXN],w[MAXN],last[MAXN],next[MAXN]; //next[i]=第i天放映的电影上一次什么时候放,last[i]=第i部电影上一次是什么时候放
//或者说last[i]=第i部电影第一次放映的时间
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
for(int i=1;i<=m;i++) scanf("%d",&w[i]);
for(int i=n;i>=1;i--)
{
next[i]=last[f[i]];
last[f[i]]=i;
}
for(int i=1;i<=m;i++)
if(last[i]) //第i个电影有放映过
{
if(!next[last[i]]) //第i个电影只放映过一次
add(1,1,n,last[i],n,w[i]); //第last[i]~n天为右端点的区间的最大权值和都要加上w[i]
else //第i个电影放映过多次
add(1,1,n,last[i],next[last[i]]-1,w[i]); //第last[i]~next[last[i]]天为右端点d区间的最大权值和都会包含w[i]
}
for(int i=1;i<=n;i++) //当前线段树的第x个位置的值是[i,x]这段时间观看的得分
{
ans=max(ans,query(1,1,n,1,n));
//然后从[i,x]这段的得分转变为[i+1,x]这段的得分
int t=next[i];
if(t)
{
add(1,1,n,i,t-1,-w[f[i]]);
if(next[t]) add(1,1,n,t,next[t]-1,w[f[i]]);
else add(1,1,n,t,n,w[f[i]]);
}
else add(1,1,n,i,n,-w[f[i]]);
}
printf("%lld\n",ans);
return 0;
}