BZOJ3747 [POI2015]Kinoman
Link
把贡献看做前缀和,第一次出现是\(+\),第二次出现是\(-\),之后的都是\(0\)。
那么预处理一下每个点后面一个和它同色的点是哪个,然后从右往左枚举左端点,线段树维护每个右端点的答案就好了。
#include<cstdio>
#include<cctype>
const int N=1000007;
using ll=long long;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
ll max(ll a,ll b){return a>b? a:b;}
int a[N],w[N],t[N],next[N];ll mx[N<<2],tag[N<<2];
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
void pushup(int p){mx[p]=max(mx[ls],mx[rs]);}
void modify(int p,ll x){tag[p]+=x,mx[p]+=x;}
void pushdown(int p){modify(ls,tag[p]),modify(rs,tag[p]),tag[p]=0;}
void update(int p,int l,int r,int L,int R,int x)
{
if(R<l||r<L) return ;
if(L<=l&&r<=R) return modify(p,x);
if(tag[p]) pushdown(p);
update(ls,l,mid,L,R,x),update(rs,mid+1,r,L,R,x),pushup(p);
}
ll query(int p,int l,int r,int L)
{
if(r<L) return 0;
if(L<=l) return mx[p];
if(tag[p]) pushdown(p);
return max(query(ls,l,mid,L),query(rs,mid+1,r,L));
}
int main()
{
int n=read(),m=read();ll ans=0;
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i) w[i]=read();
for(int i=n;i;--i) next[i]=t[a[i]]? t[a[i]]:n+1,t[a[i]]=i;
for(int l=n;l;--l)
{
update(1,1,n,l,next[l]-1,w[a[l]]);
if(next[next[l]]) update(1,1,n,next[l],next[next[l]]-1,-w[a[l]]);
ans=max(ans,query(1,1,n,l));
}
printf("%lld",ans);
}