题意:
m部电影,n天放映,第i天放映第f[i]部电影,第i部电影的好看值为w[i]。
一个区间[l,r],在第l天到第r天内,如果第i部电影只被看过一遍,那么就有w[i]的贡献,求最大贡献。
题解:
感觉是一道好题哦~(5月份没更过博啊终于打算写几篇了qwq)
从暴力入手吧,枚举左端点l,向右扫,每次cnt[f[i]]++,如果此时cnt[f[i]]=1,则加上贡献,如果cnt[f[i]]=2,则减去当前贡献,去最大值。
那么怎么去优化它呢。我们可以考虑当左端点从l移动到l+1的时候,改变的贡献。所以用tree[i]记录l到i这段区间内的贡献,放入线段树中求最大值,现在就是要考虑如何修改了,我们用nxt[i]记录从第i天往下下一个f[i]是第几天。
所以当左端点移动时,cnt[f[l]]–,那么tree[l+1…nxt[l]-1]-=w[f[l]],tree[nxt[l]…nxt[nxt[l]]-1]+=w[f[l]],在线段树上区间修改即可。
好像没什么要多注意的?(我打这道题还算挺顺利的吧
//Suplex
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1001000
using namespace std;
int n,m,f[N],w[N],nxt[N],fr[N];
long long ans;
struct Segment{
long long tag,val;
}t[N+N+N+N+N];
inline void pushdown(int p,int l,int r)
{
if(l==r) return;
t[p+p].tag+=t[p].tag;t[p+p+1].tag+=t[p].tag;
t[p+p].val+=t[p].tag;t[p+p+1].val+=t[p].tag;
t[p].tag=0;
}
void modify(int p,int l,int r,int x,int y,int delta)
{
if(t[p].tag) pushdown(p,l,r);
if(x<=l && r<=y){
t[p].tag=delta;t[p].val+=delta;
return;
}
int mid=(l+r)>>1;
if(y<=mid) modify(p+p,l,mid,x,y,delta);
else if(x>mid) modify(p+p+1,mid+1,r,x,y,delta);
else modify(p+p,l,mid,x,mid,delta),modify(p+p+1,mid+1,r,mid+1,y,delta);
t[p].val=max(t[p+p].val,t[p+p+1].val);
}
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;i--){
nxt[i]=fr[f[i]];
fr[f[i]]=i;
}
for(int i=1;i<=m;i++)
if(fr[i]){
if(!nxt[fr[i]]) modify(1,1,n,fr[i],n,w[i]);
else modify(1,1,n,fr[i],nxt[fr[i]]-1,w[i]);
}
for(int l=1;l<=n;l++){
ans=max(ans,t[1].val);
if(nxt[l]){
modify(1,1,n,l,nxt[l]-1,-w[f[l]]);
if(nxt[nxt[l]]) modify(1,1,n,nxt[l],nxt[nxt[l]]-1,w[f[l]]);
else modify(1,1,n,nxt[l],n,w[f[l]]);
}
else modify(1,1,n,l,n,-w[f[l]]);
}
printf("%lld\n",ans);
return 0;
}