2023.3.8日寄
2023.3.8 外培 Day 2 日寄
模拟赛
\(~~~~\) 总结:做不了,全tm是ynoi
After God
\(~~~~\) 好像是lxl没有公开的题,不写了。
P6780 [Ynoi2009] pmrllcsrms
题意
\(~~~~\) 给定常数 \(c\) 和长为 \(n\) 的数列,\(m\) 次操作:修改数列中某一项的值或者询问数列某个区间内长度不超过 \(c\) 的最大子段和。
\(~~~~\) \(1\leq n,m\leq 2\times 10^6\).
题解
\(~~~~\) 很自然地想到把数列分成若干长为 \(c\) 的块,然后来考虑可以怎么做。
\(~~~~\) 显然答案只会在最多两个块中间产生,那么对两种情况都进行一个讨论:
\(~~~~\) 如果答案只在一个块中产生,那么这就是一个朴素的维护区间最大子段和的问题,再把每个块的结果放到线段树上一起查询即可。
\(~~~~\) 着重来看答案在两个块中的情况:那么我们把两个块都拉下来:如果在第一个块中我们选择了一个距离左端点为 \(i\) 的点,那么第二个块中我们选择的点距离左端点应该有 \(<c-i\) ,那么倒过来也就是距离右端点 \(>i\) ,假设这个位置为 \(j\) ,那么就是要找两个位置 \(i>j\) 。并且记 \(a_i\) 为第一个块的后缀和,\(b_i\) 为第一个块的前缀和,那么 \(a_i+b_j\) 的和必须最大。很显然我们只需要在每个位置的块都维护两个序列即可。
\(~~~~\) 然后对于边角块的情况,我们发现两个块可能不能完全取完,具体来说需要分成四个部分:\(l'\) 取 \(l\) 之后,则 \(r'\) 取 \(l\) 对应的位置之前;\(l'\) 取 \(r\) 对应位置之后,则 \(r'\) 取 \(r\) 以前;\([l,l')\) 的最大子段和,\((r',r]\) 的最大子段和,以及和块间一样的取法。
\(~~~~\) 说起来好像挺简单的,那就放代码吧。
代码
查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char 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();}
x*=f;
}
template<typename T>void print(T x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
int arr[2000005];
template<typename T>inline T Min(T AA,T BB){return AA>BB?BB:AA;}
template<typename T>inline T Max(T AA,T BB){return AA<BB?BB:AA;}
struct node1{
ll L,R,Det,Sum;
node1(){}
node1(ll LL,ll RR,ll DD,ll SS){L=LL,R=RR,Det=DD,Sum=SS;}
};
inline node1 Merge(node1 a,node1 b){return node1(Max(a.L,a.Sum+b.L),Max(b.R,b.Sum+a.R),Max(Max(a.Det,b.Det),a.R+b.L),a.Sum+b.Sum);}
struct SegmentTree1{
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
node1 tr[8000005];
inline void pushUp(int p){tr[p]=Merge(tr[ls],tr[rs]);}
void Build(int p,int l,int r)
{
if(l==r){tr[p]=node1(arr[l],arr[l],arr[l],arr[l]);return;}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Modify(int p,int l,int r,int aim,int Val)
{
if(l==r){tr[p]=node1(Val,Val,Val,Val);return;}
int mid=(l+r)>>1;
if(aim<=mid) Modify(lson,aim,Val);
if(mid<aim) Modify(rson,aim,Val);
pushUp(p);
}
node1 Query(int p,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return tr[p];
int mid=(l+r)>>1;
if(lx<=mid&&mid<rx) return Merge(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
return Query(rson,lx,rx);
}
#undef ls
#undef rs
#undef lson
#undef rson
}Seg1;
ll X[2000005],Y[2000005];
struct SegmentTree2{//查询块内/块间得最大值
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
ll Maxn[8000005];
inline void pushUp(int p){Maxn[p]=Max(Maxn[ls],Maxn[rs]);}
void Build(int p,int l,int r)
{
if(l==r){Maxn[p]=X[l];return;}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Modify(int p,int l,int r,int aim,ll Val)
{
if(l==r){Maxn[p]=Val;return;}
int mid=(l+r)>>1;
if(aim<=mid) Modify(lson,aim,Val);
if(mid<aim) Modify(rson,aim,Val);
pushUp(p);
}
ll Query(int p,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return Maxn[p];
int mid=(l+r)>>1;
if(lx<=mid&&mid<rx) return Max(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
return Query(rson,lx,rx);
}
#undef ls
#undef rs
#undef lson
#undef rson
}Seg21,Seg22;
struct node3{
ll L,R,Det,Tag1,Tag2;
node3(){}
node3(ll l,ll r,ll det,ll tag1,ll tag2){L=l,R=r,Det=det,Tag1=tag1,Tag2=tag2;}
};
inline node3 Merge(node3 a,node3 b){return node3(Max(a.L,b.L),Max(a.R,b.R),Max(a.L+b.R,Max(a.Det,b.Det)),0,0);}//合并写对!!!
struct SegmentTree3{
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
vector <node3> tr;
inline void pushUp(int p){tr[p]=Merge(tr[ls],tr[rs]);}
void pushDown(int p)
{
if(tr[p].Tag1)
{
tr[ls].Tag1+=tr[p].Tag1; tr[ls].Det+=tr[p].Tag1; tr[ls].L+=tr[p].Tag1;
tr[rs].Tag1+=tr[p].Tag1; tr[rs].Det+=tr[p].Tag1; tr[rs].L+=tr[p].Tag1;
tr[p].Tag1=0;
}
if(tr[p].Tag2)
{
tr[ls].Tag2+=tr[p].Tag2; tr[ls].Det+=tr[p].Tag2; tr[ls].R+=tr[p].Tag2;
tr[rs].Tag2+=tr[p].Tag2; tr[rs].Det+=tr[p].Tag2; tr[rs].R+=tr[p].Tag2;
tr[p].Tag2=0;
}
}
void Build(int p,int l,int r)
{
if(l==r){tr[p].L=Y[l];tr[p].R=X[l];tr[p].Det=-1e18;return;}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Modify1(int p,int l,int r,int lx,int rx,ll Val)
{
if(lx<=l&&r<=rx)
{
tr[p].Tag1+=Val;tr[p].L+=Val;tr[p].Det+=Val;
return;
}
int mid=(l+r)>>1; pushDown(p);
if(lx<=mid) Modify1(lson,lx,rx,Val);
if(mid<rx) Modify1(rson,lx,rx,Val);
pushUp(p);
}
void Modify2(int p,int l,int r,int lx,int rx,ll Val)
{
if(lx<=l&&r<=rx)
{
tr[p].Tag2+=Val; tr[p].R+=Val; tr[p].Det+=Val;
return;
}
int mid=(l+r)>>1;pushDown(p);//不pushDown见祖宗
if(lx<=mid) Modify2(lson,lx,rx,Val);
if(mid<rx) Modify2(rson,lx,rx,Val);
pushUp(p);
}
node3 Query(int p,int l,int r,int lx,int rx)
{
if(lx>rx) return node3(0,0,0,0,0);
if(lx<=l&&r<=rx) return tr[p];
int mid=(l+r)>>1; pushDown(p);
if(lx<=mid&&mid<rx) return Merge(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
return Query(rson,lx,rx);
pushUp(p);
}
#undef ls
#undef rs
#undef lson
#undef rson
}Seg3[2000005];
int main() {
// freopen("girl.in","r",stdin);
// freopen("girl.out","w",stdout);
int n,m,c;read(n);read(m);read(c);c=Min(c,n);
for(int i=1;i<=n;i++) read(arr[i]);
Seg1.Build(1,1,n);int Block=(n-1)/c;
if(!Block)
{
for(int i=1,op,l,r;i<=m;i++)
{
read(op);read(l);read(r);
if(op==1) Seg1.Modify(1,1,n,l,r);
else printf("%lld\n",Max(Seg1.Query(1,1,n,l,r).Det,0ll));
}
return 0;
}
if(c==1)
{
for(int i=1;i<=n;i++) X[i]=arr[i];
Seg21.Build(1,1,n);
for(int i=1,op,l,r;i<=m;i++)
{
read(op);read(l);read(r);
if(op==1) Seg21.Modify(1,1,n,l,r);
else printf("%lld\n",Max(Seg21.Query(1,1,n,l,r),0ll));
}
return 0;
}
for(int i=0;i<Block;i++) Seg3[i].tr.resize(c<<2);
for(int i=0,l,r;i<Block;i++)
{
l=i*c+1; r=l+c-1;
ll Sum=0;
for(int j=r;j>=l;j--) Sum+=arr[j],X[j]=Sum;
Sum=0;
for(int j=l+c;j<=r+c;j++) Sum+=arr[j],Y[j-c]=Sum;
Seg3[i].Build(1,l,r);
}
for(int i=0,l,r;i<=Block;i++)
{
l=i*c+1; r=Min(l+c-1,n);
ll Sum=0,Ans=0;
for(int j=l;j<=r;j++)
{
Sum+=arr[j];
if(Sum<0) Sum=0;
Ans=Max(Ans,Sum);
}
X[i]=Ans;
}
Seg21.Build(1,0,Block);
for(int i=0;i<Block;i++) X[i]=Seg3[i].tr[1].Det;
Seg22.Build(1,0,Block-1);
for(int qwq=1,op,l,r;qwq<=m;qwq++)
{
read(op);read(l);read(r);
if(op==1)
{
int id=(l-1)/c;
ll Del=r-arr[l]; arr[l]=r;
Seg1.Modify(1,1,n,l,r);
Seg21.Modify(1,0,Block,id,Seg1.Query(1,1,n,id*c+1,(id+1)*c).Det);
if(id)//非第一块的修改都要涉及前一块
{
int L1=(id-1)*c+1,R1=id*c;
Seg3[id-1].Modify1(1,L1,R1,l-c,R1,Del);
Seg22.Modify(1,0,Block-1,id-1,Seg3[id-1].tr[1].Det);
}
if(id!=Block)//涉及自己
{
int L1=id*c+1,R1=(id+1)*c;
Seg3[id].Modify2(1,L1,R1,L1,l,Del);
Seg22.Modify(1,0,Block-1,id,Seg3[id].tr[1].Det);
}
}
else
{
int Bl=(l-1)/c,Br=(r-1)/c;
int L1=Bl*c+1,R1=(Bl+1)*c;
int L2=(Br-1)*c+1,R2=Br*c;
if(r-l+1<=c)
{
print(Max(0ll,Seg1.Query(1,1,n,l,r).Det));puts("");
continue;
}
if(Bl+1==Br)
{
ll Ans=Max(Seg1.Query(1,1,n,l,l+c-1).Det,Seg1.Query(1,1,n,r-c+1,r).Det);
Ans=Max(Ans,0ll);
if(r-l!=c)
{
Ans=Max(Ans,Seg3[Bl].Query(1,L1,R1,L1,l).L+Seg3[Bl].Query(1,L1,R1,l+1,R1).R);
Ans=Max(Ans,Seg3[Bl].Query(1,L1,R1,L1,r-c-1).L+Seg3[Bl].Query(1,L1,R1,r-c,R1).R);
Ans=Max(Ans,Seg3[Bl].Query(1,L1,R1,l,r-c-1).Det);
}
print(Ans);puts("");
}
else
{
ll Ans=Max(Seg1.Query(1,1,n,l,l+c-1).Det,Seg1.Query(1,1,n,r-c+1,r).Det);
Ans=max(Ans,0ll);
node3 L=Seg3[Bl].Query(1,L1,R1,l+1,R1),R=Seg3[Br-1].Query(1,L2,R2,L2,r-c-1);
Ans=Max(Ans,Seg3[Bl].Query(1,L1,R1,L1,l).L+L.R);
Ans=max(Ans,Seg3[Br-1].Query(1,L2,R2,r-c,R2).R+R.L);
Ans=Max(Ans,Max(L.Det,R.Det));
Ans=Max(Ans,Seg21.Query(1,0,Block,Bl+1,Br-1));
if(Bl+1<=Br-2) Ans=Max(Ans,Seg22.Query(1,0,Block-1,Bl+1,Br-2));
print(Ans);puts("");
}
}
}
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
愚者之夜
\(~~~~\) 还是lxl没公开的题,不写了。
日记
\(~~~~\) 其实感觉lxl讲题还是有意义的,没有之前那么答辩了。
\(~~~~\) 但是我必须吐槽在这个饭食,tmd居然会有比重庆摆专的食堂还不如的饭菜吗?还一顿¥20,你咋不抢呢。
\(~~~~\) 所以今天教练就带出去出去加了个餐以避免被答辩装填的饭食,然后下图是战绩。我猜这应该是我最后一次以OIER的身份线下团建了。虽然但是怎么我洗了个澡然后现在22:48我就已经快饿晕过去了,洗澡真的这么帮助挨饿吗?
\(~~~~\) 吃完感觉什么都没吃,结果等会还要喝点玄麦降降火,涂药防痘痘……
\(~~~~\) 10号就回去当维批,运批和演批了,学校生活爷回来了/kk所以现在是时候下线背维词了。另外自己可以少洗很多衣服好诶。