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天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
题解
令llas[i]表示第i号颜色上上一次出现的位置,类似地,las[i]表示第i号颜色上一次出现的位置。
考虑枚举终点r,那么假设第r天的电影种类为i,那么若l<=las[i],这个r对应的l就无法获得w[i]。
那么算法就很显然了,考虑线段树维护,对于每一个r,在线段树上【llas[i]+1,las[i]】上减去w[i],在【las[i]+1,r】上加上w[i]。r对应的答案即【1,r】的最大值。
时间复杂度O(nlogn)。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2000005; inline int read(){ int f=1,x=0; char ch; ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int c[N*2],n,m,a[N],b[N],llas[N],las[N]; struct Tre{ #define ls p*2 #define rs p*2+1 ll a[N*2+1],tag[N*2+1]; void update(int p){ a[p]=max(a[ls],a[rs]); } void down(int p,int l,int r){ int mid=(l+r)>>1; a[ls]+=tag[p]; a[rs]+=tag[p]; tag[ls]+=tag[p]; tag[rs]+=tag[p]; tag[p]=0; } void modify(int p,int l,int r,int ql,int qr,ll x){ if (ql<=l&&qr>=r){a[p]+=x; tag[p]+=x; return;} int mid=(l+r)>>1; down(p,l,r); if (ql<=mid) modify(ls,l,mid,ql,qr,x); if (qr>mid) modify(rs,mid+1,r,ql,qr,x); update(p); } ll query(int p,int l,int r,int ql,int qr){ if (ql<=l&&qr>=r){ return a[p];} ll ans=0; int mid=(l+r)>>1; down(p,l,r); if (ql<=mid) ans=max(ans,query(ls,l,mid,ql,qr)); if (qr>mid) ans=max(ans,query(rs,mid+1,r,ql,qr)); return ans; } }t; int main(){ n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=m;i++) b[i]=read(); long long ans=0; for (int i=1;i<=n;i++) { if (llas[a[i]]+1<=las[a[i]]) t.modify(1,1,n,llas[a[i]]+1,las[a[i]],-b[a[i]]); t.modify(1,1,n,las[a[i]]+1,i,b[a[i]]); long long p=t.query(1,1,n,1,i); if (p>ans) ans=p; llas[a[i]]=las[a[i]]; las[a[i]]=i; } printf("%lld\n",ans); return 0; }

浙公网安备 33010602011771号