题目大意:每个点有一个权值和一个颜色,每种颜色有一个得分,每个区间的得分为这个区间内只出现过一次的颜色的得分总和,求最大得分
说个O(nlogn)的做法吧
先预处理出对于每个节点,下一个跟他相同颜色的点的位置
然后枚举最后选的那个区间的左端点r,用一颗线段树记录对于每个r,这个区间的得分是多少
当左端点l向右移时,设当前左端点颜色为i,相当于把以l到nxt[i]-1为结尾的区间的得分减w[i],再把以nxt[i]到nxt[nxt[i]]-1为结尾的区间的得分加w[i]
然后查询一下以l+1到n为结尾的区间的得分的最大值,就是以l为为开头的所有区间的得分的最大值
速度奇慢无比
不知道有没有O(N)的做法
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lll long long
using namespace std;
int l[4000001],r[4000001];
lll t[4000001],w[4000001];
void build(lll now,lll ll,lll rr)
{
l[now]=ll;r[now]=rr;
if(ll==rr) return;
lll mid=(ll+rr)>>1;
build(now<<1,ll,mid);
build(now<<1|1,mid+1,rr);
}
void pdn(lll x)
{
t[x<<1]+=t[x];w[x<<1]+=t[x];
t[x<<1|1]+=t[x];w[x<<1|1]+=t[x];
t[x]=0;
}
void pup(lll x)
{
w[x]=max(w[x<<1],w[x<<1|1]);
}
void change(lll now,lll ll,lll rr,lll v)
{
if(l[now]==ll&&r[now]==rr)
{
t[now]+=v;
w[now]+=v;
return;
}
if(t[now]) pdn(now);
lll mid=(l[now]+r[now])>>1;
if(rr<=mid) change(now<<1,ll,rr,v);
else if(ll>mid) change(now<<1|1,ll,rr,v);
else
{
change(now<<1,ll,mid,v);
change(now<<1|1,mid+1,rr,v);
}
pup(now);
}
lll check(lll now,lll ll,lll rr)
{
if(l[now]==ll&&r[now]==rr) return w[now];
if(t[now]) pdn(now);
lll mid=(l[now]+r[now])>>1;
if(rr<=mid) return check(now<<1,ll,rr);
else if(ll>mid) return check(now<<1|1,ll,rr);
else return max(check(now<<1,ll,mid),check(now<<1|1,mid+1,rr));
}
int a[1000001],nxt[1000001],pre[1000001];
lll b[1000001];
int main()
{
lll n,m;
scanf("%lld%lld",&n,&m);
lll i,j;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=n;i>=1;i--)
{
nxt[i]=pre[a[i]];
pre[a[i]]=i;
}
build(1,1,n);
for(i=1;i<=m;i++)
{
scanf("%lld",&b[i]);
if(pre[i])
{
if(nxt[pre[i]]) change(1,pre[i],nxt[pre[i]]-1,b[i]);
else change(1,pre[i],n,b[i]);
}
}
lll ans=0;
for(i=1;i<=n;i++)
{
ans=max(ans,check(1,i,n));
if(nxt[i])
{
change(1,i,nxt[i]-1,-b[a[i]]);
if(nxt[nxt[i]]) change(1,nxt[i],nxt[nxt[i]]-1,b[a[i]]);
else change(1,nxt[i],n,b[a[i]]);
}
else change(1,i,n,-b[a[i]]);
}
printf("%lld",ans);
}