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);
}
posted @ 2020-01-14 19:13  Shiina_Mashiro  阅读(126)  评论(0编辑  收藏  举报