CF1649F Serious Business

题目大意

给你一个 3 3 3 n n n列的网格,每个网格上有一个数,从左上角出发,终点在右下角,每次只能往右走和往下走,一开始第二行的网格是不允许经过的,有 q q q个操作,可以花费 k k k的代价使 l l l r r r列的第二行的网格变得允许经过,问你经过网格数的总和-花费的总代价的最大值

题解

( 2 , i ) (2,i) (2,i)进入第三行的话接下来的决策就确定了,考虑设 f i f_i fi表示走到 ( 2 , i ) (2,i) (2,i)的最大分数,对于一个操作 ( l , r , k ) (l,r,k) (l,r,k)和一个 i i i,有两个可行转移
f i = max ⁡ ( f i , f l − 1 − s 2 , l − 1 − k + s 2 , i ) f_i=\max(f_i,f_{l-1}-s_{2,l-1}-k+s_{2,i}) fi=max(fi,fl1s2,l1k+s2,i)
f i = max ⁡ ( f i , max ⁡ l ≤ j ≤ i s 1 , j − s 2 , j − 1 − k + s 2 , i ) f_i=\max(f_i,\max_{l\le j\le i}{s_{1,j}-s_{2,j-1}}-k+s_{2,i}) fi=max(fi,ljimaxs1,js2,j1k+s2,i)
其中, s i , j s_{i,j} si,j表示第 i i i行前 j j j列的和, s 2 , i s_{2,i} s2,i是定值可以查询 f i f_i fi时再加上,上面的转移可以再线段树上打一个区间 max ⁡ \max max标记,第二个操作可以在线段树上打一个左儿子对右儿子的标记,一开始要对操作按左端点排序以保证在更新 f i f_i fi之前求出 f l − 1 f_{l-1} fl1,时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

c o d e code code

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
void read(int &res)
{
	res=0;int x=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') x=-x;ch=getchar();}
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
	res*=x;
}
const int N=5e5+1000;
const ll inf=1e16;
int n,q;
ll s1[N+10],s2[N+10],s3[N+10],f[N+10];
struct SEG
{
	ll maxs,maxf,tag1,tag2;
}t[N<<2|1];
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
void build(int p=1,int l=1,int r=n)
{
	t[p].maxf=t[p].tag1=-inf,t[p].tag2=inf;
	if(l==r)
	{
		t[p].maxs=s1[l]-s2[l-1];
		return;
	}
	build(ls,l,mid),build(rs,mid+1,r);
	t[p].maxs=max(t[ls].maxs,t[rs].maxs);
}
void pushdown(int p)
{
	if(t[p].tag1!=-inf)
	{
		t[ls].tag1=max(t[ls].tag1,t[p].tag1),t[ls].maxf=max(t[ls].maxf,t[p].tag1);
		t[rs].tag1=max(t[rs].tag1,t[p].tag1),t[rs].maxf=max(t[rs].maxf,t[p].tag1);
		t[p].tag1=-inf;
	}
	if(t[p].tag2!=inf)
	{
		t[rs].tag1=max(t[rs].tag1,t[ls].maxs-t[p].tag2),t[rs].maxf=max(t[rs].maxf,t[ls].maxs-t[p].tag2);
		t[ls].tag2=min(t[ls].tag2,t[p].tag2),t[rs].tag2=min(t[rs].tag2,t[p].tag2),t[p].tag2=inf;
	}
}
void updatemax(int L,int R,ll k,int p=1,int l=1,int r=n)
{
	if(L<=l&&r<=R)
	{
		t[p].tag1=max(t[p].tag1,k),t[p].maxf=max(t[p].maxf,k);
		return;
	}
	pushdown(p);
	if(L<=mid) updatemax(L,R,k,ls,l,mid);
	if(R>mid) updatemax(L,R,k,rs,mid+1,r);
	t[p].maxf=max(t[ls].maxf,t[rs].maxf);
}
ll updaters(int L,int R,ll k,ll qmaxs,int p=1,int l=1,int r=n)
{
	if(L<=l&&r<=R)
	{
		t[p].tag1=max(t[p].tag1,qmaxs-k),t[p].maxf=max(t[p].maxf,qmaxs-k),t[p].tag2=min(t[p].tag2,k);
		return t[p].maxs;
	}
	pushdown(p);
	if(L<=mid) qmaxs=max(qmaxs,updaters(L,R,k,qmaxs,ls,l,mid));
	if(R>mid) qmaxs=max(qmaxs,updaters(L,R,k,qmaxs,rs,mid+1,r));
	t[p].maxf=max(t[ls].maxf,t[rs].maxf);
	return qmaxs;
}
ll query(int x,int p=1,int l=1,int r=n)
{
	if(l==r) return max(t[p].maxs-t[p].tag2,t[p].maxf);
	pushdown(p);
	if(x<=mid) return query(x,ls,l,mid);
	else return query(x,rs,mid+1,r);
}
#undef ls
#undef rs
#undef mid
struct qst
{
	int l,r,k;
}p[N+10];
bool cmp(qst a,qst b){return a.l<b.l||(a.l==b.l&&a.r<b.r);}
int main()
{
	read(n),read(q);
	for(int i=1,x;i<=n;i++) read(x),s1[i]=s1[i-1]+x;
	for(int i=1,x;i<=n;i++) read(x),s2[i]=s2[i-1]+x;
	for(int i=1,x;i<=n;i++) read(x),s3[i]=s3[i-1]+x;
	build();
	for(int i=1;i<=q;i++) read(p[i].l),read(p[i].r),read(p[i].k);
	sort(p+1,p+1+q,cmp);
	ll ans=-inf;
	int r=1;
	for(int i=1;i<=q;i++)
	{
		while(r<p[i].l) f[r]=query(r)+s2[r],ans=max(ans,f[r]+s3[n]-s3[r-1]),r++;
		if(p[i].l!=1) updatemax(p[i].l,p[i].r,f[p[i].l-1]-s2[p[i].l-1]-p[i].k);
		updaters(p[i].l,p[i].r,p[i].k,-inf);
	}
	while(r<=n) f[r]=query(r)+s2[r],ans=max(ans,f[r]+s3[n]-s3[r-1]),r++;
	printf("%lld",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值