題意:
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;
}