题意:有$n$个杯子,杯子$i$可以装价值为$w_i$(可正可负)的饮料,一开始所有杯子都是空的,在某些时刻,一些杯子会被加满饮料,你可以选择在一些时刻把$n$个杯子中的饮料喝完(一旦喝了就必须喝完$n$杯)并获得其价值,现在要最大化获得的总价值
设$f_i$表示考虑$1\cdots i$时刻并在$i$时刻喝饮料的最大价值,则$f_i=\max\limits_{j\leq i}f_{j-1}+s_{j\cdots i}$,这里$s_{j\cdots i}$表示在$j\cdots i$时刻被加满的杯子的饮料价值之和(被加多次只算一次价值)
设固定$i$时,$g_j=f_{j-1}+s_{j\cdots i}$,考虑从$i-1$变为$i$对$g$的影响,枚举每个在$i$时被加满的杯子$x$,如果它上次被加满的时间为$l$,那么$g_{l+1\cdots i}$会增加$w_x$,所以用线段树维护$g$即可
#include<stdio.h> #include<vector> #include<algorithm> using namespace std; typedef long long ll; const ll inf=9223372036854775807ll; ll mx[2000010],d[2000010]; void pushup(int x){ mx[x]=max(mx[x<<1],mx[x<<1|1]); } void ad(int x,ll v){ d[x]+=v; mx[x]+=v; } void pushdown(int x){ if(d[x]){ ad(x<<1,d[x]); ad(x<<1|1,d[x]); d[x]=0; } } void modify(int L,int R,ll v,int l,int r,int x){ if(L<=l&&r<=R)return ad(x,v); pushdown(x); int mid=(l+r)>>1; if(L<=mid)modify(L,R,v,l,mid,x<<1); if(mid<R)modify(L,R,v,mid+1,r,x<<1|1); pushup(x); } ll query(int L,int R,int l,int r,int x){ if(L<=l&&r<=R)return mx[x]; pushdown(x); int mid=(l+r)>>1; ll res=-inf; if(L<=mid)res=max(res,query(L,R,l,mid,x<<1)); if(mid<R)res=max(res,query(L,R,mid+1,r,x<<1|1)); return res; } vector<int>p[500010],t[500010]; int w[500010],at[500010],v[500010]; ll f[500010]; int main(){ int n,N,i,k,x; ll ans; scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",w+i); N=0; for(i=1;i<=n;i++){ scanf("%d",&k); t[i].push_back(0); while(k--){ scanf("%d",&x); t[i].push_back(x); v[++N]=x; } } sort(v+1,v+N+1); N=unique(v+1,v+N+1)-v-1; for(i=1;i<=n;i++){ for(int&x:t[i]){ if(x)p[x=lower_bound(v+1,v+N+1,x)-v].push_back(i); } } ans=0; for(i=1;i<=N;i++){ for(int x:p[i])modify(t[x][at[x]++]+1,i,w[x],1,N,1); f[i]=query(1,i,1,N,1); ans=max(ans,f[i]); if(i<N)modify(i+1,i+1,f[i],1,N,1); } printf("%lld",ans); }