Description
共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
Input
第一行两个整数n,m(1<=m<=n<=1000000)。
第二行包含n个整数f[1],f[2],…,f[n](1<=f[i]<=m)。
第三行包含m个整数w[1],w[2],…,w[m](1<=w[j]<=1000000)。
Output
输出观看且仅观看过一次的电影的好看值的总和的最大值。
Sample Input
9 4
2 3 1 1 4 1 2 4 1
5 3 6 6
2 3 1 1 4 1 2 4 1
5 3 6 6
Sample Output
15
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
Solution
记一个数组$pre[i]$,表示$i$这个位置上的数上一次出现的位置在哪。
从左往右扫一边,区间$pre[i]+1$到$i$加上当前数的权值,$pre[pre[i]]+1$到$pre[i]$这段位置减去这个数的权值。然后查询区间$[1,i]$,取查询的$max$即为答案。
Code
#include<iostream>
#include<cstdio>
#define N (1000009)
#define LL long long
using namespace std; struct Sgt{LL max,add;}Segt[N<<];
int n,m,f[N],w[N],pre[N],a[N];
LL ans; void Pushdown(int now)
{
Segt[now<<].add+=Segt[now].add;
Segt[now<<].max+=Segt[now].add;
Segt[now<<|].add+=Segt[now].add;
Segt[now<<|].max+=Segt[now].add;
Segt[now].add=;
} void Update(int now,int l,int r,int l1,int r1,int k)
{
if (l>r1 || r<l1) return;
if (l1<=l && r<=r1)
{
Segt[now].add+=k;
Segt[now].max+=k;
return;
}
Pushdown(now);
int mid=(l+r)>>;
Update(now<<,l,mid,l1,r1,k);
Update(now<<|,mid+,r,l1,r1,k);
Segt[now].max=max(Segt[now<<].max,Segt[now<<|].max);
} LL Query(int now,int l,int r,int l1,int r1)
{
if (l>r1 || r<l1) return -;
if (l1<=l && r<=r1) return Segt[now].max;
Pushdown(now);
int mid=(l+r)>>;
return max(Query(now<<,l,mid,l1,r1),Query(now<<|,mid+,r,l1,r1));
} int main()
{
scanf("%d%d",&n,&m);
for (int i=; i<=n; ++i)
scanf("%d",&f[i]), pre[i]=a[f[i]], a[f[i]]=i;
for (int i=; i<=m; ++i)
scanf("%d",&w[i]);
for (int i=; i<=n; ++i)
{
Update(,,n,pre[i]+,i,w[f[i]]);
Update(,,n,pre[pre[i]]+,pre[i],-w[f[i]]);
ans=max(ans,Query(,,n,,i));
}
printf("%lld\n",ans);
}