3747: [POI2015]Kinoman
Time Limit: 60 Sec Memory Limit: 128 MB
Submit: 2031 Solved: 840
[Submit][Status][Discuss]
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。
我们枚举每个点作为右端点的时候的答案即可。
然后我们线段树维护当前的点为右端点,每个点分别为左端点的时候的答案,一直更新最大值即可。
我们记录每个点前面一样的出现的位置。然后很容易可以算出来,每个点i加入区间的答案。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m,f[N],pre[N],w[N],vis[N]; ll res;
struct node{int l,r; ll mx,lazy;}t[N<<2];
inline void push_up(int p){t[p].mx=max(t[p<<1].mx,t[p<<1|1].mx);}
inline void push_down(int p){
if(t[p].lazy){
t[p<<1].lazy+=t[p].lazy,t[p<<1|1].lazy+=t[p].lazy;
t[p<<1].mx+=t[p].lazy,t[p<<1|1].mx+=t[p].lazy; t[p].lazy=0;
}
}
void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(l==r) return ; int mid=l+r>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
}
void change(int p,int l,int r,int v){
if(l>r) return ;
if(t[p].l==l&&t[p].r==r){t[p].lazy+=v; t[p].mx+=v; return ;}
push_down(p); int mid=t[p].l+t[p].r>>1;
if(r<=mid) change(p<<1,l,r,v);
else if(l>mid) change(p<<1|1,l,r,v);
else change(p<<1,l,mid,v),change(p<<1|1,mid+1,r,v);
push_up(p);
}
signed main(){
cin>>n>>m; build(1,1,n);
for(int i=1;i<=n;i++) scanf("%d",&f[i]),pre[i]=vis[f[i]],vis[f[i]]=i;
for(int i=1;i<=m;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
change(1,pre[i]+1,i,w[f[i]]);
if(pre[i]) change(1,pre[pre[i]]+1,pre[i],-w[f[i]]);
res=max(res,t[1].mx);
}
cout<<res<<endl;
return 0;
}