Jack-Oran的王国

任天高云阔,任逍遥自在,朗朗乾坤有心做伴,便有鸢飞鱼跃。

BZOJ3747-[POI2015]Kinoman

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

Sample Output

15
样例解释:

观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。

题解

设next[i]表示第i天电影下次播放时间。对于每部电影k,从k到next[k]-1要加w[k],或者从next[k]到next[next[k]]-1要加w[k],以此类推。

所以先枚举每一部电影i,将i到next[i]-1加w[i]。

再枚举每一个左端点k,先把k到next[k]-1减去w[f[k]],然后把next[k]到next[next[k]]-1加上w[f[k]]。

每个循环求最大值。

Code:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int read()
{
    int x=0;char ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,m;
int f[1000005],w[1000005];
int last[1000005],next[1000005];
ll ans;
struct seg{
	int l,r;
	ll tag,mx;
}a[4000005];
void build(int k,int l,int r)
{
	a[k].l=l;a[k].r=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
void pushdown(int k)
{
	int l=a[k].l,r=a[k].r;
	if(l==r)return;
	a[k<<1].tag+=a[k].tag;
	a[k<<1|1].tag+=a[k].tag;
	a[k<<1].mx+=a[k].tag;
	a[k<<1|1].mx+=a[k].tag;
	a[k].tag=0;
}
void add(int k,int x,int y,int value) 
{
	if(a[k].tag)pushdown(k);
	int l=a[k].l,r=a[k].r;
	if(l==x&&y==r)
	{
		a[k].tag=value;
		a[k].mx+=value;
		return;
	}
	int mid=(l+r)>>1;
	if(y<=mid)add(k<<1,x,y,value);else
	if(x>mid)add(k<<1|1,x,y,value);else
	add(k<<1,x,mid,value),add(k<<1|1,mid+1,y,value);
	a[k].mx=max(a[k<<1].mx,a[k<<1|1].mx);
}
int main()
 {
	 n=read();m=read();
	 for(int i=1;i<=n;i++)f[i]=read();
	 for(int i=1;i<=m;i++)w[i]=read();
	 for(int i=n;i;i--)
	 {
		 next[i]=last[f[i]];
		 last[f[i]]=i;
	 }
	 build(1,1,n);
	 for(int i=1;i<=m;i++)
		 if(last[i])
		 {
			 if(!next[last[i]])add(1,last[i],n,w[i]);
			 else add(1,last[i],next[last[i]]-1,w[i]);
		 }
	 for(int i=1;i<=n;i++)
	 {
		 ans=max(ans,a[1].mx);
		 int t=next[i];
		 if(t)
		 {
			 add(1,i,t-1,-w[f[i]]);
			 if(next[t])add(1,t,next[t]-1,w[f[i]]);
			 else add(1,t,n,w[f[i]]);
		 }
		 else add(1,i,n,-w[f[i]]);
	 }
	 printf("%lld\n",ans);
	 return 0;
}
阅读更多
版权声明:不念过去,不畏未来,一切都是过眼云烟。 https://blog.csdn.net/qq_34531807/article/details/80342597
所属专栏: BZOJ
想对作者说点什么? 我来说一句

bzoj 3747 [POI2015]Kinoman

线段树

chai_jing chai_jing

2016-11-29 15:11:11

阅读数:188

没有更多推荐了,返回首页

不良信息举报

BZOJ3747-[POI2015]Kinoman

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭