Description
共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
题解:
先预处理出对于每个节点,下一个跟他相同颜色的点的位置
然后枚举最后选的那个区间的左端点r,用一颗线段树记录对于每个r,这个区间的得分是多少。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int N=1000005;
int n,m;
int last[N],nex[N];
int a[N],val[N];
struct node{
ll mx,lazy;
int l,r;
}tr[4000005];
void bt(int x,int l,int r)
{
tr[x].l=l;
tr[x].r=r;
tr[x].mx=tr[x].lazy=0;
int mid=(l+r)>>1;
if(l<r)
{
bt(x<<1,l,mid);
bt(x<<1|1,mid+1,r);
}
}
void update(int k)
{
int l=tr[k].l,r=tr[k].r;
if (l==r) return;
ll lazy=tr[k].lazy; tr[k].lazy=0;
tr[k<<1].lazy+=lazy; tr[k<<1|1].lazy+=lazy;
tr[k<<1].mx+=lazy; tr[k<<1|1].mx+=lazy;
}
void change(int rt,int l,int r,int l1,int r1,int c)
{
if(tr[rt].lazy) update(rt);
if(l==l1&&r==r1){tr[rt].mx+=c;tr[rt].lazy+=c;return;}
int mid=(l+r)>>1;
if(r1<=mid) change(rt<<1,l,mid,l1,r1,c);
else if(l1>mid) change(rt<<1|1,mid+1,r,l1,r1,c);
else change(rt<<1,l,mid,l1,mid,c),change(rt<<1|1,mid+1,r,mid+1,r1,c);
tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
memset(last,0,sizeof(last));
memset(nex,0,sizeof(nex));
for(int i=n;i>=1;i--)
{
nex[i]=last[a[i]];
last[a[i]]=i;
}
bt(1,1,n);
for(int j=1;j<=m;j++)
{
scanf("%d",&val[j]);
if(last[j])
{
if(!nex[last[j]]) change(1,1,n,last[j],n,val[j]);
else change(1,1,n,last[j],nex[last[j]]-1,val[j]);
}
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,tr[1].mx);
if(nex[i])
{
change(1,1,n,i,nex[i]-1,-val[a[i]]);
if(nex[nex[i]]) change(1,1,n,nex[i],nex[nex[i]]-1,val[a[i]]);
else change(1,1,n,nex[i],n,val[a[i]]);
}
else change(1,1,n,i,n,-val[a[i]]);
}
printf("%lld\n",ans);
}