[ARC056D]サケノミ

题意:有$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);
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9832525.html
arc
今日推荐