题目大意
给你一个 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,fl−1−s2,l−1−k+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,l≤j≤imaxs1,j−s2,j−1−k+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}
fl−1,时间复杂度为
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;
}