解题思路
遇到这种选区间的题,很多时候都是枚举左端点,然后计算右端点。考虑枚举左端点之后如何维护,设下一部和(i)这个位置的电影相同的为(nxt[i]),那么如果左端点从(i)移动到(i+1),([i+1,nxt[i]-1])这段区间会减少(w[f[i]]),而([nxt[i],nxt[nxt[i]]-1)会增加(w[f[i]]),发现可以线段树维护。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1000005;
typedef long long LL;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return f?x:-x;
}
inline LL max(LL x,LL y){return x>y?x:y;}
int n,m,pre[N],w[N],nxt[N],cnt[N],f[N];
LL ans,Max[N<<2],tag[N<<2];
inline void pushdown(int x){
Max[x<<1]+=tag[x];Max[x<<1|1]+=tag[x];
tag[x<<1]+=tag[x];tag[x<<1|1]+=tag[x];
tag[x]=0;
}
void update(int x,int l,int r,int L,int R,LL k){
if(L>R) return ;
if(L<=l && r<=R) {Max[x]+=k;tag[x]+=k;return ;}
int mid=(l+r)>>1;if(tag[x]) pushdown(x);
if(L<=mid) update(x<<1,l,mid,L,R,k);
if(mid<R) update(x<<1|1,mid+1,r,L,R,k);
Max[x]=max(Max[x<<1],Max[x<<1|1]);
}
LL query(int x,int l,int r,int L,int R){
if(L<=l && r<=R) return Max[x];
int mid=(l+r)>>1;if(tag[x]) pushdown(x);
if(L>mid) return query(x<<1|1,mid+1,r,L,R);
else if(R<=mid) return query(x<<1,l,mid,L,R);
else return max(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
signed main(){
n=rd(),m=rd();int x;
for(int i=1;i<=n;i++){
x=rd();f[i]=x;
if(pre[x]) nxt[pre[x]]=i;
pre[x]=i;
}
LL now=0;
for(int i=1;i<=m;i++) w[i]=rd();
for(int i=1;i<=n;i++){
if(!cnt[f[i]]) now+=w[f[i]];
else if(cnt[f[i]]==1) now-=w[f[i]];
cnt[f[i]]++;update(1,1,n,i,i,now);
}
for(int i=1;i<=n;i++){
ans=max(ans,query(1,1,n,i,n));
if(!nxt[i]) update(1,1,n,i+1,n,-w[f[i]]);
else update(1,1,n,i+1,nxt[i]-1,-w[f[i]]);
if(nxt[i] && !nxt[nxt[i]]) update(1,1,n,nxt[i],n,w[f[i]]);
else update(1,1,n,nxt[i],nxt[nxt[i]]-1,w[f[i]]);
}
printf("%lld
",ans);
return 0;
}