【bzoj3747】[POI2015]Kinoman
2014年12月19日1,4611
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。
【分析】
转自hzwer
不得不说我的代码超级慢
线段树经典题
nxt[i]记录第i天的电影下次播放时间
枚举区间左端点,线段树维护每个位置作为右端点的答案
考虑l-r的左端点变为l+1
发现l到nxt[l]-1的答案减少w[f[l]]
而nxt[l]到nxt[nxt[l]]-1增加w[f[l]]
线段树维护,支持区间修改以及查询最大值
【代码】
//bzoj 3747
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define lson num<<1,t[num].l,mid
#define rson num<<1|1,mid+1,t[num].r
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=1000005;
ll ans;
int n,m,k,L,R;
int next[mxn],last[mxn],f[mxn],w[mxn];
struct node {int l,r;ll mx,ad;} t[mxn<<2];
inline void pushdown(int num)
{
if(!t[num].ad) return;
int ls=num<<1,rs=num<<1|1;
t[ls].ad+=t[num].ad,t[rs].ad+=t[num].ad;
t[ls].mx+=t[num].ad,t[rs].mx+=t[num].ad;
t[num].ad=0;
}
inline void build(int num,int l,int r)
{
t[num].l=l,t[num].r=r;
if(l==r) return;
int mid=l+r>>1;
build(lson),build(rson);
}
inline void add(int num)
{
if(L<=t[num].l && t[num].r<=R)
{
t[num].mx+=k;
t[num].ad+=k;
return;
}
pushdown(num);
int mid=t[num].l+t[num].r>>1;
if(L<=mid) add(num<<1);
if(mid<R) add(num<<1|1);
t[num].mx=max(t[num<<1].mx,t[num<<1|1].mx);
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&f[i]);
fo(i,1,m) scanf("%d",&w[i]);
for(i=n;i;i--)
next[i]=last[f[i]],last[f[i]]=i;
build(1,1,n);
fo(i,1,m) if(last[i])
{
L=last[i],k=w[i];
if(next[last[i]]) R=next[last[i]]-1,add(1);
else R=n,add(1);
}
fo(i,1,n)
{
ans=max(ans,t[1].mx);
L=i,k=w[f[i]];
if(next[i])
{
if(next[next[i]]) L=next[i],R=next[next[i]]-1,add(1);
else L=next[i],R=n,add(1);
k=-k,L=i,R=next[i]-1,add(1);
}
else k=-k,R=n,add(1);
}
printf("%lld\n",ans);
return 0;
}