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],…,fn。
第三行包含m个整数w[1],w[2],…,wm。
Output
输出观看且仅观看过一次的电影的好看值的总和的最大值。
Sample Input
9 4
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。
HINT
传送门
不会做orz……
对于某一个数字x,在它只出现一次的区间内相当于都增加了w[x](权值)
记x之前的位置是pre[x],那么遇到一个x,
就在(pre[x],x]的区间都加上w[x],并且在(pre[pre[x]],pre[x]]的区间减去w[x],。
每次在[1,x]查询一下某一位置的最大值。
线段树维护就好了……
好巧啊。。完全没想到= =
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int
N=1000005;
int n,m,f[N],pre[N],TMP[N];
ll w[N];
struct Segment{ll MAX,mark;}seg[N<<2];
void up(int u){
int l=u<<1,r=u<<1|1;
seg[u].MAX=max(seg[l].MAX,seg[r].MAX);
}
void down(int u){
if (seg[u].mark){
int l=u<<1,r=u<<1|1;
seg[l].mark+=seg[u].mark;
seg[l].MAX+=seg[u].mark;
seg[r].mark+=seg[u].mark;
seg[r].MAX+=seg[u].mark;
seg[u].mark=0LL;
}
}
void update(int id,int l,int r,int gl,int gr,ll num){
if (l>=gl && r<=gr){
seg[id].mark+=num;
seg[id].MAX+=num;
return;
}
down(id);
int mid=(l+r)>>1;
if (gl<=mid) update(id<<1,l,mid,gl,gr,num);
if (gr>mid) update(id<<1|1,mid+1,r,gl,gr,num);
up(id);
}
ll query(int id,int l,int r,int gl,int gr){
if (l>=gl && r<=gr) return seg[id].MAX;
down(id);
int mid=(l+r)>>1;ll t=0LL;
if (gl<=mid) t=max(t,query(id<<1,l,mid,gl,gr));
if (gr>mid) t=max(t,query(id<<1|1,mid+1,r,gl,gr));
return t;
}
int main(){
scanf("%d%d",&n,&m);
memset(TMP,0,sizeof(TMP));
for (int i=1;i<=n;i++){
scanf("%d",&f[i]);
pre[i]=TMP[f[i]];
TMP[f[i]]=i;
}
pre[0]=-1;
for (int i=1;i<=m;i++) scanf("%lld",&w[i]);
ll ans=0LL;
for (int i=1;i<=n;i++){
update(1,1,n,pre[i]+1,i,w[f[i]]);
if (pre[i])
update(1,1,n,pre[pre[i]]+1,pre[i],-w[f[i]]);
ans=max(ans,query(1,1,n,1,i));
}
printf("%lld\n",ans);
return 0;
}