您的位置:首页 > 其它

bzoj 3747: [POI2015]Kinoman 线段树

2017-04-19 20:46 423 查看

题意

共有m部电影,编号为1~m,第i部电影的好看值为w[i]。

在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。

你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。

n,m<=1000000,w[i]<=1000000

分析

还挺有意思的一道题目。

考虑一个左端点的贡献.那么我们可以对第一次出现的数的权值赋为正,第二次出现的权值赋为负,其余赋为0,然后求一个最大的前缀和即可。

预处理每个数的下一次出现位置,然后用线段树维护最大前缀和即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N=1000005;

int n,m,w
,f
,ls
,nx
;
struct tree{LL tag,mx;}t[N*5];

void pushdown(int d,int l,int r)
{
if (l==r||!t[d].tag) return;
LL w=t[d].tag;t[d].tag=0;
t[d*2].tag+=w;t[d*2].mx+=w;
t[d*2+1].tag+=w;t[d*2+1].mx+=w;
}

void ins(int d,int l,int r,int x,int y,int z)
{
if (x>y) return;
pushdown(d,l,r);
if (l==x&&r==y)
{
t[d].tag+=z;t[d].mx+=z;
return;
}
int mid=(l+r)/2;
ins(d*2,l,mid,x,min(y,mid),z);
ins(d*2+1,mid+1,r,max(x,mid+1),y,z);
t[d].mx=max(t[d*2].mx,t[d*2+1].mx);
}

LL query(int d,int l,int r,int x,int y)
{
if (x>y) return 0;
pushdown(d,l,r);
if (l==x&&r==y) return t[d].mx;
int mid=(l+r)/2;
return max(query(d*2,l,mid,x,min(y,mid)),query(d*2+1,mid+1,r,max(x,mid+1),y));
}

int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&f[i]);
for (int i=1;i<=m;i++) scanf("%d",&w[i]);
for (int i=n;i>=1;i--)
if (!ls[f[i]]) nx[i]=n+1,ls[f[i]]=i;
else nx[i]=ls[f[i]],ls[f[i]]=i;
LL ans=0;
for (int i=n;i>=1;i--)
{
if (nx[i]==n+1) ins(1,1,n,i,n,w[f[i]]);
else ins(1,1,n,nx[i],nx[nx[i]]-1,-w[f[i]]),ins(1,1,n,i,nx[i]-1,w[f[i]]);
ans=max(ans,query(1,1,n,i,n));
}
printf("%lld",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: