共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
标签:poi2015 stream get fine led using ret appear 包含
15
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
在沈队的博客上看了这道题感觉不错,在来做的这道题。
这道题,我认为主要有两个难点:
1:
对于每一天,下一次该天播放的电影下次播放时间的处理。
2:
将电影对后面的影响转换到线段树上。
首先,我们定义线段树为区间最大值。
我们将每次电影的价值的表达形式变为:
这次该种电影到下次该种电影的区间都加上w[i]。
这样转换之后,我们在枚举每次播放电影的时候只需要取到1到n的区间中最大值,就是答案。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define il inline
#define db double
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
il int gi()
{
int x=0,y=1;
char ch=getchar();
while(ch<‘0‘||ch>‘9‘)
{
if(ch==‘-‘)
y=-1;
ch=getchar();
}
while(ch>=‘0‘&&ch<=‘9‘)
{
x=x*10+ch-‘0‘;
ch=getchar();
}
return x*y;
}
il ll gl()
{
ll x=0,y=1;
char ch=getchar();
while(ch<‘0‘||ch>‘9‘)
{
if(ch==‘-‘)
y=-1;
ch=getchar();
}
while(ch>=‘0‘&&ch<=‘9‘)
{
x=x*10+ch-‘0‘;
ch=getchar();
}
return x*y;
}
struct node
{
int l,r;
ll s;
}c[4000045];
ll lazy[4000045];
il void pushdown(int rt)
{
if(lazy[rt])
{
lazy[rt<<1]+=lazy[rt];
lazy[(rt<<1)+1]+=lazy[rt];
c[rt<<1].s+=lazy[rt];
c[(rt<<1)+1].s+=lazy[rt];
lazy[rt]=0;
}
}
void add(int rt,int l,int r,int L,int R,ll num)
{
if(L<=l&&R>=r)
{
c[rt].s+=num;
lazy[rt]+=num;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)
add(rt<<1,l,mid,L,R,num);
if(R>mid)
add((rt<<1)+1,mid+1,r,L,R,num);
c[rt].s=max(c[rt<<1].s,c[(rt<<1)+1].s);
}
int next[1000045];//i th day next time to show
int day[1000045];//num i film last time appear
int f[1000045];
ll w[1000045];
int main()
{
int n=gi(),m=gi();
for(int i=1;i<=n;i++)
f[i]=gi();
for(int i=1;i<=m;i++)
w[i]=gl();
for(int i=n;i>=1;i--)//必须逆向才能求出来
{
next[i]=day[f[i]];
day[f[i]]=i;
}
for(int i=1;i<=m;i++)//把每种电影第一段区间加上
{
if(!day[i])
continue;
if(next[day[i]])
add(1,1,n,day[i],next[day[i]]-1,w[i]);
else//只出现了一次
add(1,1,n,day[i],n,w[i]);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,c[1].s);
if(next[i])
{
add(1,1,n,i,next[i]-1,-w[f[i]]);//把当前到下次出现的区间减去
if(next[next[i]])//为枚举到next[i]做准备
add(1,1,n,next[i],next[next[i]]-1,w[f[i]]);
else
add(1,1,n,next[i],n,w[f[i]]);
}
else
add(1,1,n,i,n,-w[f[i]]);
}
printf("%lld\n",ans);
return 0;
}
BZOJ 3747: [POI2015]Kinoman 线段树
标签:poi2015 stream get fine led using ret appear 包含
原文地址:http://www.cnblogs.com/gshdyjz/p/7682874.html