经典做法,处理出next[i]表示下一个相同的地方。
注意是算出现且仅出现一次的地方。
每次加一个颜色就是[i,next[i]-1]这个区间+价值
删除一个颜色就是[i,next[i-1]]这个区间-价值
所以用一个线段树维护每个位置的答案,支持区间加和查询就可以了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 1000100
using namespace std;
struct yts
{
long long tag,mx;
int l,r;
}t[4*maxn];
int a[maxn],last[maxn],next[maxn];
long long b[maxn];
int n,m;
long long ans;
void build(int i,int l,int r)
{
t[i].l=l;t[i].r=r;
if (l==r) return;
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
}
void add(int i,long long d)
{
t[i].mx+=d;t[i].tag+=d;
}
void release(int i)
{
if (t[i].l==t[i].r) return;
if (t[i].tag)
{
add(i*2,t[i].tag);add(i*2+1,t[i].tag);
t[i].tag=0;
}
}
long long query(int i,int l,int r)
{
if (l<=t[i].l && t[i].r<=r) return t[i].mx;
release(i);
int mid=(t[i].l+t[i].r)/2;
long long ans=0;
if (l<=mid) ans=max(ans,query(i*2,l,r));
if (mid<r) ans=max(ans,query(i*2+1,l,r));
return ans;
}
void modify(int i,int l,int r,long long d)
{
if (l<=t[i].l && t[i].r<=r) {add(i,d);return;}
release(i);
int mid=(t[i].l+t[i].r)/2;
if (l<=mid) modify(i*2,l,r,d);
if (mid<r) modify(i*2+1,l,r,d);
t[i].mx=max(t[i*2].mx,t[i*2+1].mx);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
next[last[a[i]]]=i;
last[a[i]]=i;
}
for (int i=1;i<=m;i++) next[last[i]]=n+1;
for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
build(1,1,n);
memset(last,0,sizeof(last));
for (int i=1;i<=n;i++)
if (!last[a[i]])
{
modify(1,i,next[i]-1,b[a[i]]);
last[a[i]]=i;
}
for (int i=1;i<=n;i++)
{
ans=max(ans,query(1,i,n));
modify(1,i,next[i]-1,-b[a[i]]);
if (next[i]!=n+1) modify(1,next[i],next[next[i]]-1,b[a[i]]);
}
printf("%lld\n",ans);
return 0;
}